Merge commit '6305524f2f6d2de6897ea45ad291788ca8015ad2'

This commit is contained in:
Alexa Amundson
2025-11-21 16:07:55 -06:00
13 changed files with 5286 additions and 45 deletions

3
.eslintrc.json Normal file
View File

@@ -0,0 +1,3 @@
{
"extends": ["next/core-web-vitals"]
}

5
next-env.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

4870
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@
"lint": "next lint" "lint": "next lint"
}, },
"engines": { "engines": {
"node": ">=20" "node": ">=20",
"dev": "docusaurus start", "dev": "docusaurus start",
"build": "npm run generate:meta && docusaurus build", "build": "npm run generate:meta && docusaurus build",
"start": "node scripts/serve.js", "start": "node scripts/serve.js",
@@ -21,5 +21,12 @@
"next": "14.2.4", "next": "14.2.4",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1" "react-dom": "^18.3.1"
},
"devDependencies": {
"@types/node": "^24.10.1",
"@types/react": "^19.2.6",
"eslint": "^8.57.0",
"eslint-config-next": "^14.2.4",
"typescript": "^5.9.3"
} }
} }

View File

@@ -0,0 +1,45 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
const navItems = [
{ href: '/', label: 'Overview' },
{ href: '/architecture', label: 'Architecture' },
{ href: '/services', label: 'Services' },
{ href: '/agents', label: 'Agents' },
{ href: '/deployment', label: 'Deployment' },
];
export default function DocsLayout({ title, intro, children }) {
const router = useRouter();
return (
<div className="docs-shell">
<aside className="docs-sidebar">
<div className="docs-brand">BlackRoad OS</div>
<nav>
<ul className="docs-nav">
{navItems.map((item) => {
const isActive = router.pathname === item.href;
return (
<li key={item.href} className={isActive ? 'active' : undefined}>
<Link href={item.href}>{item.label}</Link>
</li>
);
})}
</ul>
</nav>
</aside>
<div className="docs-main">
{(title || intro) && (
<header className="docs-header">
{title && <h1>{title}</h1>}
{intro && <p className="subtle">{intro}</p>}
</header>
)}
<div className="docs-content">{children}</div>
</div>
</div>
);
}

View File

@@ -4,4 +4,8 @@ export const osServices = [
{ id: "web", name: "Web Frontend", description: "Public-facing site and status." }, { id: "web", name: "Web Frontend", description: "Public-facing site and status." },
{ id: "prism-console", name: "Prism Console", description: "Internal console for monitoring and control." }, { id: "prism-console", name: "Prism Console", description: "Internal console for monitoring and control." },
{ id: "docs", name: "Docs", description: "You are here." } { id: "docs", name: "Docs", description: "You are here." }
{ id: "operator", name: "Operator Service", description: "Jobs, queues, workers." },
{ id: "web", name: "Web Frontend", description: "Public site and status." },
{ id: "prism-console", name: "Prism Console", description: "Internal operator console." },
{ id: "docs", name: "Docs", description: "Documentation and knowledge base." }
]; ];

View File

@@ -1,4 +1,5 @@
import Head from 'next/head'; import Head from 'next/head';
import DocsLayout from '../components/DocsLayout';
export default function Agents() { export default function Agents() {
return ( return (
@@ -27,6 +28,51 @@ export default function Agents() {
</div> </div>
</section> </section>
</main> </main>
</Head>
<DocsLayout
title="Agents"
intro="How automated agents and the Operator service coordinate work across the platform."
>
<section className="section">
<h2>Agent orchestration</h2>
<p className="subtle">
Agents are background workers that react to events, queue tasks, and stitch together service-to-service
workflows. They are designed to be composable so new flows can be added without disturbing core surfaces.
</p>
</section>
<section className="section">
<h2>Role of the Operator service</h2>
<div className="card">
<p>
The Operator service owns scheduling, retries, and execution guarantees. It accepts jobs from frontends,
APIs, or other agents, then dispatches them to the correct worker pool.
</p>
<ul className="docs-list">
<li>
<strong>Queues and topics:</strong> Jobs are grouped by domain (payments, notifications, reporting) so
that throughput can be tuned independently.
</li>
<li>
<strong>Health surfaces:</strong> Standard endpoints like <code>/health</code> and <code>/version</code>
provide quick visibility into the runtime state.
</li>
<li>
<strong>Pluggable agents:</strong> Each agent can be deployed or updated without touching the rest of the
stack, keeping the system resilient.
</li>
</ul>
</div>
</section>
<section className="section">
<h2>Where to go next</h2>
<p className="subtle">
Use this section as a placeholder while deeper specs, sequencing diagrams, and runbooks are added. The
sidebar navigation keeps the structure stable as more agent documentation grows.
</p>
</section>
</DocsLayout>
</> </>
); );
} }

View File

@@ -1,4 +1,39 @@
import Head from 'next/head'; import Head from 'next/head';
import DocsLayout from '../components/DocsLayout';
import { osServices } from '../config/services';
const layers = [
{
title: 'Core',
description: 'Ledger, state, and internal APIs that hold the operating model together.',
},
{
title: 'Operator',
description: 'Job orchestration, background processing, and agent execution.',
},
{
title: 'Web',
description: 'Public-facing entry point, marketing, and status communication.',
},
{
title: 'Prism Console',
description: 'Operational console for humans to inspect jobs and flows.',
},
{
title: 'Docs',
description: 'Shared knowledge base that aligns the other layers.',
},
];
const diagram = `
Users & Operators
|
Web / Prism Console / Docs
|
API Edge
|
Core ←→ Operator
`;
export default function Architecture() { export default function Architecture() {
return ( return (
@@ -25,10 +60,48 @@ export default function Architecture() {
<p className="subtle"> <p className="subtle">
This section will outline the layers, data flows, and handoffs between services. Detailed walkthroughs This section will outline the layers, data flows, and handoffs between services. Detailed walkthroughs
and diagrams are on the way. and diagrams are on the way.
<DocsLayout
title="Architecture"
intro="How the layers of BlackRoad OS stitch together across frontends, services, and operators."
>
<section className="section">
<h2>Layers at a glance</h2>
<div className="card-grid">
{layers.map((layer) => (
<div key={layer.title} className="card">
<h3>{layer.title}</h3>
<p className="subtle">{layer.description}</p>
</div>
))}
</div>
</section>
<section className="section">
<h2>Rough flow</h2>
<div className="card">
<pre className="diagram" aria-label="Architecture flow diagram">{diagram}</pre>
<p className="subtle" style={{ marginTop: '12px' }}>
Requests and jobs start at the user-facing surfaces, pass through the API edge, and land on the Core and
Operator services. Documentation and operational tooling keep each surface aligned.
</p> </p>
</div> </div>
</section> </section>
</main>
<section className="section">
<h2>Service lineage</h2>
<p className="subtle">
Each layer maps directly to a named service so that deployments, observability, and onboarding stay
consistent:
</p>
<ul className="docs-list">
{osServices.map((service) => (
<li key={service.id}>
<strong>{service.name}</strong> {service.description}
</li>
))}
</ul>
</section>
</DocsLayout>
</> </>
); );
} }

View File

@@ -1,4 +1,5 @@
import Head from 'next/head'; import Head from 'next/head';
import DocsLayout from '../components/DocsLayout';
export default function Deployment() { export default function Deployment() {
return ( return (
@@ -27,6 +28,50 @@ export default function Deployment() {
</div> </div>
</section> </section>
</main> </main>
</Head>
<DocsLayout
title="Deployment"
intro="High-level look at how BlackRoad OS services move from source to runtime."
>
<section className="section">
<h2>Pipeline overview</h2>
<p className="subtle">
Deployments follow a simple path: GitHub holds source control, Railway builds and ships services, and
Cloudflare fronts the public edges. This keeps environments consistent while leaving room for more
automation later.
</p>
</section>
<section className="section">
<h2>Service expectations</h2>
<div className="card">
<ul className="docs-list">
<li>
<strong>Railway configuration:</strong> Each service tracks a <code>railway.json</code> file with its
environment setup and deploy target.
</li>
<li>
<strong>Health endpoints:</strong> Standard endpoints like <code>/health</code>, <code>/info</code>, and
<code>/version</code> allow Railway and Cloudflare to verify readiness.
</li>
<li>
<strong>Domains:</strong> Services sit under the <code>*.blackroad.systems</code> namespace so routing stays
predictable.
</li>
</ul>
</div>
</section>
<section className="section">
<h2>Flow in brief</h2>
<div className="card">
<p>GitHub Railway build/deploy Cloudflare edge BlackRoad OS services</p>
<p className="subtle" style={{ marginTop: '8px' }}>
Keep code, config, and health surfaces aligned to make this path repeatable across all five services.
</p>
</div>
</section>
</DocsLayout>
</> </>
); );
} }

View File

@@ -10,59 +10,41 @@ const links = [
{ href: '/agents', title: 'Agents', description: 'Background actors, automations, and how they interact with the OS.' }, { href: '/agents', title: 'Agents', description: 'Background actors, automations, and how they interact with the OS.' },
{ href: '/deployment', title: 'Deployment', description: 'Environment notes, release procedures, and operational readiness.' }, { href: '/deployment', title: 'Deployment', description: 'Environment notes, release procedures, and operational readiness.' },
]; ];
import DocsLayout from '../components/DocsLayout';
import { osServices } from '../config/services';
export default function Home() { export default function Home() {
return ( return (
<> <>
<Head> <Head>
<title>BlackRoad OS Documentation</title> <title>Overview | BlackRoad OS Docs</title>
<meta <meta name="description" content="High-level overview of BlackRoad OS." />
name="description"
content="Reference for architecture, services, and integration of BlackRoad OS."
/>
</Head> </Head>
<header className="hero"> <DocsLayout
<h1>BlackRoad OS Documentation</h1> title="Overview"
<p>Reference for architecture, services, and integration of BlackRoad OS.</p> intro="A concise entry point to how BlackRoad OS is organized and what it offers."
</header> >
<main className="main">
<section className="section"> <section className="section">
<h2>Start exploring</h2> <h2>What is BlackRoad OS?</h2>
<div className="card-grid"> <p className="subtle">
{links.map((link) => ( BlackRoad OS is a coordinated platform that aligns frontends, services, and operators under a single
<div key={link.href} className="card"> operating model. It standardizes deployments, endpoints, and workflows so teams can ship features across
<h3>{link.title}</h3> the stack with confidence.
<p className="subtle">{link.description}</p>
<p>
<Link href={link.href}>Open {link.title} </Link>
</p> </p>
</div>
))}
</div>
</section> </section>
<section className="section"> <section className="section">
<h2>Service metadata</h2> <h2>Core services</h2>
<p className="subtle">Values provided via shared configuration.</p> <p className="subtle">Five services anchor the platform. Each ships with a health surface and shared deployment playbook.</p>
<ul className="list-inline"> <ul className="docs-list">
<li> {osServices.map((service) => (
<strong>Service ID:</strong> {serviceConfig.SERVICE_ID} <li key={service.id}>
</li> <strong>{service.name}:</strong> {service.description}
<li>
<strong>Service Name:</strong> {serviceConfig.SERVICE_NAME}
</li>
<li>
<strong>Service Base URL:</strong> {serviceConfig.SERVICE_BASE_URL}
</li>
<li>
<strong>OS Root:</strong> {serviceConfig.OS_ROOT}
</li> </li>
))}
</ul> </ul>
</section> </section>
</DocsLayout>
<div className="footer">BlackRoad OS Docs stay aligned across frontends, APIs, and operators.</div>
<EnvironmentInfo />
</main>
</> </>
); );
} }

View File

@@ -6,6 +6,18 @@ const serviceLinks = {
'prism-console': 'https://console.blackroad.systems', 'prism-console': 'https://console.blackroad.systems',
docs: 'https://docs.blackroad.systems', docs: 'https://docs.blackroad.systems',
}; };
import DocsLayout from '../components/DocsLayout';
import { osServices } from '../config/services';
const responsibilities = {
core: 'Maintains ledger, internal state, and foundational APIs.',
operator: 'Runs background jobs, queues, and agent workloads.',
web: 'Publishes the public site, marketing, and uptime signals.',
'prism-console': 'Exposes operator tooling for investigations and job review.',
docs: 'Hosts this documentation hub and shared guides.',
};
const defaultEndpoints = ['/health', '/info', '/version'];
export default function Services() { export default function Services() {
return ( return (
@@ -51,9 +63,43 @@ export default function Services() {
</p> </p>
</div> </div>
))} ))}
<DocsLayout
title="Services"
intro="Catalog of the core BlackRoad OS services, their responsibilities, and the shared endpoints exposed by each."
>
<section className="section">
<div className="card">
<table className="table">
<thead>
<tr>
<th>Service</th>
<th>ID</th>
<th>Responsibility</th>
<th>Example endpoints</th>
</tr>
</thead>
<tbody>
{osServices.map((service) => (
<tr key={service.id}>
<td>{service.name}</td>
<td>
<span className="badge">{service.id}</span>
</td>
<td>{responsibilities[service.id]}</td>
<td>
<ul className="endpoint-list">
{defaultEndpoints.map((endpoint) => (
<li key={`${service.id}-${endpoint}`}>{endpoint}</li>
))}
</ul>
</td>
</tr>
))}
</tbody>
</table>
</div> </div>
</section> </section>
</main> </DocsLayout>
</> </>
); );
} }

View File

@@ -60,6 +60,39 @@ body {
.nav-link { .nav-link {
color: var(--text); color: var(--text);
padding: 8px 12px; padding: 8px 12px;
.docs-shell {
display: grid;
grid-template-columns: 240px 1fr;
min-height: 100vh;
}
.docs-sidebar {
background: rgba(17, 24, 49, 0.8);
border-right: 1px solid var(--border);
padding: 24px 18px;
position: sticky;
top: 0;
height: 100vh;
}
.docs-brand {
font-weight: 700;
letter-spacing: 0.6px;
margin-bottom: 20px;
}
.docs-nav {
list-style: none;
padding: 0;
margin: 0;
display: grid;
gap: 10px;
}
.docs-nav li a {
color: var(--text);
display: block;
padding: 10px 12px;
border-radius: 8px; border-radius: 8px;
border: 1px solid transparent; border: 1px solid transparent;
} }
@@ -93,6 +126,28 @@ body {
.layout-content { .layout-content {
padding-top: 12px; padding-top: 12px;
.docs-nav li.active a {
font-weight: 700;
border-color: var(--accent);
background: rgba(59, 130, 246, 0.08);
text-decoration: none;
}
.docs-main {
padding: 32px 32px 64px;
}
.docs-header h1 {
margin: 0 0 8px;
}
.docs-header p {
margin: 0;
}
.docs-content {
margin-top: 28px;
max-width: 960px;
} }
a { a {
@@ -193,6 +248,36 @@ a:hover {
padding: 12px; padding: 12px;
} }
.docs-list {
list-style: none;
padding: 0;
margin: 12px 0 0;
display: grid;
gap: 12px;
}
.docs-list li {
background: rgba(17, 24, 49, 0.6);
border: 1px solid var(--border);
border-radius: 10px;
padding: 12px 14px;
}
.endpoint-list {
list-style: none;
padding: 0;
margin: 0;
display: grid;
gap: 6px;
}
.diagram {
white-space: pre;
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
margin: 0;
line-height: 1.5;
}
.subtle { .subtle {
color: var(--muted); color: var(--muted);
} }

32
tsconfig.json Normal file
View File

@@ -0,0 +1,32 @@
{
"compilerOptions": {
"target": "ESNext",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
"**/*.js",
"**/*.jsx"
],
"exclude": [
"node_modules"
]
}