Skip to content

Commit 0ceb8cd

Browse files
SafetyMPSage Hart
andauthored
feat: autonomous company r2 with governance plane (#42)
Ship orchestrator-backed company day on a shared firm store, console HITL, honest sim/live providers, and July-2026 governance (three-layer authz, ASI adversarial matrix, AIBOM, OTel GenAI spans) for corpos-autonomous-company-r2. Co-authored-by: Sage Hart <sagehart@MacBookAir.attlocal.net>
1 parent 03e4a4f commit 0ceb8cd

42 files changed

Lines changed: 1446 additions & 170 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.corp-harness/site.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"schema": "corporate-site-site/v1",
33
"site_id": "corpos",
4-
"corporate_program": "corpos-autonomous-company",
5-
"corporate_handoff_sha256": "0953f106918b8ffe1a61e30f3a69afbd0498bd44df695ff6b3c15c4b6915af68",
4+
"corporate_program": "corpos-autonomous-company-r2",
5+
"corporate_handoff_sha256": "15052d5905e7e73687465dbf6d1d801d31105a7c7abfd33ea50ceb2652dcbeb6",
66
"verify_argv": ["./scripts/harness/verify.sh"],
77
"adversarial_argv": ["./scripts/harness/adversarial.sh"]
88
}

.env.example

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@
1515
# ── HTTP ─────────────────────────────────────────────────────────────
1616
# PORT=3000
1717

18-
# ── Model providers (optional; SimulationProvider used in CI) ────────
18+
# ── Model providers ──────────────────────────────────────────────────
19+
# Default is SimulationProvider (health.mode=simulation).
20+
# Live HttpLLMProvider only when BOTH are set:
21+
# CORPOS_ALLOW_LIVE=1
22+
# OPENROUTER_API_KEY=...
23+
# OPENROUTER_API_KEY alone does NOT switch agents to live reasoning.
24+
# CORPOS_ALLOW_LIVE=0
1925
# OPENROUTER_API_KEY=
20-
# OPENROUTER_MODEL=openrouter/owl-alpha
21-
# ANTHROPIC_API_KEY=
22-
# OPENAI_API_KEY=
26+
# OPENROUTER_MODEL=openai/gpt-4o-mini

CONTEXT.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
# Project context
22

33
CorpOS is the open reference implementation of an autonomous company:
4-
firm model + work contracts + control plane. Simulation-first.
4+
firm model + work contracts + control plane + governance plane (July 2026).
55

66
Workspaces: `packages/core`, `packages/mcp-knowledge`, `apps/api`, `apps/console`.
77

88
Commands: `npm run dev`, `npm test`, `npm run scenario`, `npm run audit:verify`,
99
`./scripts/harness/verify.sh`, `./scripts/harness/adversarial.sh`.
1010

11-
Site id: `corpos`. Program: `corpos-autonomous-company`.
11+
Site id: `corpos`. Program: `corpos-autonomous-company-r2`.
12+
Live LLM requires `CORPOS_ALLOW_LIVE=1` and `OPENROUTER_API_KEY`.

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
# CorpOS
22

3-
> Reference implementation of an **autonomous company** — firm model, work contracts, and a policy-gated control plane.
3+
> Reference implementation of an **autonomous company** — firm model, work contracts, policy-gated control plane, and a July 2026 **governance plane** (PDP/PEP, three-layer authz, OTel GenAI, OWASP ASI / NIST RMF crosswalk).
44
55
[![CI](https://github.com/SafetyMP/CorpOS/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/SafetyMP/CorpOS/actions/workflows/ci.yml)
66
[![CodeQL](https://github.com/SafetyMP/CorpOS/actions/workflows/codeql.yml/badge.svg?branch=main)](https://github.com/SafetyMP/CorpOS/actions/workflows/codeql.yml)
77
[![OpenSSF Scorecard](https://github.com/SafetyMP/CorpOS/actions/workflows/scorecard.yml/badge.svg?branch=main)](https://scorecard.dev/viewer/?uri=github.com/SafetyMP/CorpOS)
88
[![License: Apache-2.0](https://img.shields.io/github/license/SafetyMP/CorpOS)](LICENSE)
99
[![Node](https://img.shields.io/badge/node-%E2%89%A522-339933?logo=node.js&logoColor=white)](#quick-start)
1010

11-
CorpOS shows how a firm operates when department agents do most work and **humans govern by exception**. Autonomy is earned from evidence, not granted in prompts.
11+
CorpOS shows how a firm operates when department agents do most work and **humans govern by exception** (Approve / Reject / Kill in the ops console). Autonomy is earned from evidence, not granted in prompts. Interop protocols (MCP) are transport; **community governance (G1–G6)** lives in the firm.
1212

1313
> **Scope:** Reference architecture and runnable demo — **not** a production-hardened SaaS. See [SECURITY.md](SECURITY.md).
1414
15-
Read the thesis: [`docs/future-of-the-firm.md`](docs/future-of-the-firm.md).
15+
Default mode is **simulation** (`SimulationProvider`) for deterministic CI. Live LLM (`HttpLLMProvider`) only when `CORPOS_ALLOW_LIVE=1` and `OPENROUTER_API_KEY` are set — `/api/health.mode` never lies.
16+
17+
Read the thesis: [`docs/future-of-the-firm.md`](docs/future-of-the-firm.md). Governance crosswalk: [`docs/governance-crosswalk.md`](docs/governance-crosswalk.md). AIBOM: [`docs/aibom.json`](docs/aibom.json).
1618

1719
**Jump to:** [Demo](#demo) · [Quick start](#quick-start) · [Architecture](#architecture) · [Contributing](CONTRIBUTING.md) · [Security](SECURITY.md) · [ADRs](docs/adr/)
1820

apps/api/src/app.ts

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,16 @@ import {
1010
decideException,
1111
departments,
1212
exceptions,
13+
listSpans,
14+
resolveProvider,
1315
runCompanyDay,
1416
traces,
1517
type Company,
1618
} from "@corpos/core";
1719
import { eq } from "drizzle-orm";
20+
import fs from "node:fs";
21+
import path from "node:path";
22+
import { fileURLToPath } from "node:url";
1823

1924
function requireAuth(c: { req: { header: (n: string) => string | undefined } }): boolean {
2025
if (process.env.CORPOS_MODE !== "shared") return true;
@@ -24,15 +29,27 @@ function requireAuth(c: { req: { header: (n: string) => string | undefined } }):
2429
return header === `Bearer ${expected}`;
2530
}
2631

27-
export function buildApp(company: Company): Hono {
32+
function loadAibom(): unknown {
33+
const candidates = [
34+
path.resolve(process.cwd(), "docs/aibom.json"),
35+
path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../docs/aibom.json"),
36+
];
37+
for (const p of candidates) {
38+
if (fs.existsSync(p)) return JSON.parse(fs.readFileSync(p, "utf8"));
39+
}
40+
return { error: "aibom missing" };
41+
}
42+
43+
export function buildApp(company: Company, mode: "simulation" | "live" = "simulation"): Hono {
2844
const app = new Hono();
2945
app.use("*", cors());
3046

3147
app.get("/api/health", (c) =>
3248
c.json({
3349
ok: true,
34-
mode: process.env.OPENROUTER_API_KEY ? "live" : "simulation",
50+
mode,
3551
product: "autonomous-company-reference",
52+
provider: mode === "live" ? "HttpLLMProvider" : "SimulationProvider",
3653
}),
3754
);
3855

@@ -57,9 +74,19 @@ export function buildApp(company: Company): Hono {
5774

5875
app.post("/api/exceptions/:id/decide", async (c) => {
5976
if (!requireAuth(c)) return c.json({ error: "dashboard authentication required" }, 401);
60-
const body = await c.req.json<{ decision: "approved" | "rejected"; by?: string }>();
61-
await decideException(company, c.req.param("id"), body.decision, body.by ?? "operator");
62-
return c.json({ ok: true });
77+
const body = await c.req.json<{
78+
decision: "approved" | "rejected";
79+
by?: string;
80+
dissentReason?: string;
81+
}>();
82+
const out = await decideException(
83+
company,
84+
c.req.param("id"),
85+
body.decision,
86+
body.by ?? "operator",
87+
body.dissentReason,
88+
);
89+
return c.json({ ok: true, ...out });
6390
});
6491

6592
app.post("/api/kill", async (c) => {
@@ -70,11 +97,51 @@ export function buildApp(company: Company): Hono {
7097
});
7198

7299
app.post("/api/company-day", async (c) => {
73-
const { result, company: ephemeral } = await runCompanyDay({ dbPath: ":memory:" });
74-
ephemeral.close();
100+
let autoApproveException = true;
101+
try {
102+
const body = await c.req.json<{ autoApproveException?: boolean }>();
103+
if (body.autoApproveException === false) autoApproveException = false;
104+
} catch {
105+
/* empty body */
106+
}
107+
const { result } = await runCompanyDay({ company, autoApproveException });
75108
return c.json(result);
76109
});
77110

111+
app.get("/api/governance", async (c) => {
112+
const aibom = loadAibom();
113+
const spans = listSpans().slice(-50);
114+
const recentDenies = (await company.audit.verify()).ok;
115+
const ctrl = (
116+
await company.db.select().from(controlState).where(eq(controlState.id, "global"))
117+
)[0];
118+
return c.json({
119+
aibom,
120+
spans,
121+
auditOk: recentDenies,
122+
killed: Boolean(ctrl?.killed),
123+
asiControls: {
124+
ASI01: "untrusted KB/CRM boundary",
125+
ASI02: "fail-closed gateway + draft/settle",
126+
ASI03: "three-layer authz",
127+
ASI04: "AIBOM + policy bundle hash",
128+
ASI05: "no shell/eval tools registered",
129+
ASI06: "untrusted memory/context flags",
130+
ASI07: "handoff envelopes with depth/origin",
131+
ASI08: "capital/kill/depth caps",
132+
ASI09: "L3+ exception HITL",
133+
ASI10: "kill switch + trust demotion",
134+
},
135+
nistRmf: {
136+
GOVERN: "policy PDP/PEP + enforcement modes",
137+
MAP: "AIBOM inventory",
138+
MEASURE: "OTel GenAI spans + trust ledger",
139+
MANAGE: "exceptions, kill, compensators",
140+
},
141+
note: "Crosswalk is pedagogical; not a certification claim.",
142+
});
143+
});
144+
78145
app.get("/api/traces/:taskId", async (c) => {
79146
const row = (await company.db.select().from(traces)).find(
80147
(t) => t.taskId === c.req.param("taskId"),
@@ -119,6 +186,11 @@ export function buildApp(company: Company): Hono {
119186
return app;
120187
}
121188

122-
export async function createDefaultCompany(): Promise<Company> {
123-
return createCompany({ dbPath: process.env.CORPOS_DB });
189+
export async function createDefaultCompany(): Promise<{
190+
company: Company;
191+
mode: "simulation" | "live";
192+
}> {
193+
const { mode } = resolveProvider();
194+
const company = await createCompany({ dbPath: process.env.CORPOS_DB });
195+
return { company, mode };
124196
}

apps/api/src/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ if (scenario) {
1313
process.exit(result.ok ? 0 : 1);
1414
}
1515

16-
const company = await createDefaultCompany();
17-
const app = buildApp(company);
16+
const { company, mode } = await createDefaultCompany();
17+
const app = buildApp(company, mode);
1818

1919
const here = path.dirname(fileURLToPath(import.meta.url));
2020
const consoleDist = path.resolve(here, "../../console/dist");
2121
app.use("/*", serveStatic({ root: consoleDist }));
2222

2323
const port = Number(process.env.PORT ?? 3000);
2424
serve({ fetch: app.fetch, port }, () => {
25-
console.log(`CorpOS ops console on http://localhost:${port}`);
25+
console.log(`CorpOS ops console on http://localhost:${port} (mode=${mode})`);
2626
});

0 commit comments

Comments
 (0)