mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-10-02 12:54:03 +00:00
logicsrc.com/.well-known/openprofile.md was a 404 while every relay in the OpenMCP catalog, Obscura included, named it as its operator. The site that publishes the spec now serves its own file: Kind organization, accounts (GitHub, blog, the catalog, the Obscura relay so the trust chain closes both ways), topics, projects and contact. The root layout carries rel="openprofile" and every response carries the same relation as a Link header, per the spec's discovery rules. skill.md and llms.txt point at it. The OpenMCP spec page and docs/openmcp.md now lead with curl -fsSL https://openmcp.logicsrc.com/install.sh | sh instead of npx, and the descriptor examples name logicsrc.com's real profile rather than one profullstack.com never served. Claude-Session: https://claude.ai/code/session_01Qh2dieNyPZ4Hx5g3XNJ1Eo Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
85 lines
3.6 KiB
TypeScript
85 lines
3.6 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 discovery for responses that are not HTML (feeds, JSON,
|
|
// the specs as Markdown): the same relation the root layout puts in <head>.
|
|
{
|
|
key: "Link",
|
|
value: `<${(process.env.PUBLIC_URL ?? "https://logicsrc.com").replace(/\/$/, "")}/.well-known/openprofile.md>; rel="openprofile"`,
|
|
},
|
|
];
|
|
|
|
/**
|
|
* 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 } : [];
|
|
}
|
|
|
|
const nextConfig: NextConfig = {
|
|
async headers() {
|
|
return [{ source: "/:path*", headers: securityHeaders }];
|
|
},
|
|
async rewrites() {
|
|
return buildRewrites();
|
|
},
|
|
};
|
|
|
|
export default nextConfig;
|