mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-10-01 20:33:50 +00:00
A new LogicSRC spec in the catalogs family. One heading, an identity block (Kind, Web, Repo, Operator, License, Extends, Updated), one line, then eleven layers: Languages, Runtimes, Interfaces (one ### per way in: web, api, cli, tui, mcp, desktop, mobile, worker, extension, bot), Data, Services, Modules, Tooling, Hosting, Auth, Conventions, Not. An item is one bullet: name, version, an optional status word (trial, hold, leaving) and a role. Extends inherits a parent file, sections replace, Conventions and Not accumulate. Rule 8 is the reading rule for agents: use what is listed, prefer listed over new, ask before adding a layer or a service, never add a Not, keep the file true. Discovery at OpenStack.md in the repo, /.well-known/openstack.md, rel=openstack, or a platform path. JSON is derived and never the source. logicsrc.com serves its own at /.well-known/openstack.md: the route extracts the worked example from docs/openstack.md, and a contract test holds the two together and checks the file follows its own rules. rel=openstack in <head> and in the Link header beside openprofile. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
104 lines
4.3 KiB
TypeScript
104 lines
4.3 KiB
TypeScript
import type { NextConfig } from "next";
|
|
|
|
// The CommandBoard API (boards, tasks, plugins, /health) runs as its own
|
|
// service. In the old custom server.js it was mounted in-process; here we proxy
|
|
// those paths to it via rewrites. Our own /api routes (hire-us, oauth/coinpay,
|
|
// webhooks) are filesystem routes and match before these afterFiles rewrites.
|
|
const commandboardApiUrl = process.env.COMMANDBOARD_API_URL;
|
|
|
|
// The credentials app (apps/pwa) is also its own service, and it owns the CLI
|
|
// login flow: `logicsrc login` talks to /cli/*, and the browser half of that
|
|
// flow needs a session, which lives behind /auth/*.
|
|
//
|
|
// Proxying those paths is what lets all of it live on logicsrc.com. Pointing
|
|
// the apex at the pwa instead would take the marketing site down with it, since
|
|
// the pwa serves `/` too; a subdomain would work but needs a Railway custom
|
|
// domain and a DNS record. This needs neither, and it makes the CLI's default
|
|
// origin (https://logicsrc.com) correct as it already stands.
|
|
const credentialsAppUrl = process.env.CREDENTIALS_APP_URL;
|
|
|
|
const securityHeaders = [
|
|
// HSTS — site is HTTPS-only behind Railway. No `preload` (irreversible).
|
|
{ key: "Strict-Transport-Security", value: "max-age=31536000; includeSubDomains" },
|
|
{ key: "X-Content-Type-Options", value: "nosniff" },
|
|
{ key: "X-Frame-Options", value: "SAMEORIGIN" },
|
|
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
|
|
{ key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()" },
|
|
// OpenProfile.md and OpenStack.md discovery for responses that are not HTML
|
|
// (feeds, JSON, the specs as Markdown): the same relations the root layout
|
|
// puts in <head>.
|
|
{
|
|
key: "Link",
|
|
value: [
|
|
`<${(process.env.PUBLIC_URL ?? "https://logicsrc.com").replace(/\/$/, "")}/.well-known/openprofile.md>; rel="openprofile"`,
|
|
`<${(process.env.PUBLIC_URL ?? "https://logicsrc.com").replace(/\/$/, "")}/.well-known/openstack.md>; rel="openstack"`,
|
|
].join(", "),
|
|
},
|
|
];
|
|
|
|
/**
|
|
* The paths the credentials app owns.
|
|
*
|
|
* `/api/me` and `/api/credshare/*` are named individually, and the caller must
|
|
* place these BEFORE the CommandBoard `/api/:path*` catch-all — otherwise the
|
|
* catch-all swallows them and sends CLI auth to the wrong service.
|
|
*/
|
|
export function credentialsRewrites(base: string) {
|
|
return [
|
|
// the device-code and loopback login flows themselves
|
|
{ source: "/cli/:path*", destination: `${base}/cli/:path*` },
|
|
// identity, and the credential-sharing API the CLI uses once logged in
|
|
{ source: "/api/me", destination: `${base}/api/me` },
|
|
{ source: "/api/credshare/:path*", destination: `${base}/api/credshare/:path*` },
|
|
// /cli/authorize and /cli/device sit behind requireAuth, so an
|
|
// unauthenticated visitor gets redirected here to sign in. Without this the
|
|
// browser half of the flow dead-ends on a 404.
|
|
{ source: "/auth/:path*", destination: `${base}/auth/:path*` },
|
|
];
|
|
}
|
|
|
|
/** CommandBoard's paths. The `/api` entry is a catch-all, so it goes last. */
|
|
export function commandboardRewrites(base: string) {
|
|
return [
|
|
{ source: "/health", destination: `${base}/health` },
|
|
{ source: "/api/:path*", destination: `${base}/api/:path*` },
|
|
];
|
|
}
|
|
|
|
/** Built as a function so the ordering above is testable without booting Next. */
|
|
export function buildRewrites(
|
|
credentials = credentialsAppUrl,
|
|
commandboard = commandboardApiUrl,
|
|
) {
|
|
const afterFiles = [
|
|
...(credentials ? credentialsRewrites(credentials.replace(/\/$/, "")) : []),
|
|
...(commandboard ? commandboardRewrites(commandboard.replace(/\/$/, "")) : []),
|
|
];
|
|
return afterFiles.length ? { afterFiles } : [];
|
|
}
|
|
|
|
/**
|
|
* Paths that moved. `/agent-swarm` was the AgentSwarm "coming soon" band on
|
|
* the home page; the spec it promised is OpenFleet, which has its own landing
|
|
* page. Permanent, so the old URL in the sitemap and in links keeps working.
|
|
*/
|
|
export function buildRedirects() {
|
|
return [
|
|
{ source: "/agent-swarm", destination: "/openfleet", permanent: true },
|
|
{ source: "/openprofile/skills", destination: "/openskill", permanent: true }
|
|
];
|
|
}
|
|
|
|
const nextConfig: NextConfig = {
|
|
async headers() {
|
|
return [{ source: "/:path*", headers: securityHeaders }];
|
|
},
|
|
async rewrites() {
|
|
return buildRewrites();
|
|
},
|
|
async redirects() {
|
|
return buildRedirects();
|
|
},
|
|
};
|
|
|
|
export default nextConfig;
|