Review bootstrap and connectivity ADRs with immutable history
This commit is contained in:
parent
b202ed02f1
commit
dcdec162d6
8 changed files with 1040 additions and 6 deletions
|
|
@ -4,9 +4,11 @@ type: architecture-decision-record
|
|||
title: "Custodian Agent Runtime — v0.1 Bootstrap Design"
|
||||
status: accepted
|
||||
owner: the-custodian
|
||||
revision: "accepted-1"
|
||||
last_reviewed: "2026-03-12"
|
||||
revision: "accepted-2"
|
||||
last_reviewed: "2026-10-11"
|
||||
review_interval: 6m
|
||||
reviewed_by: "codex — technical source/currency review"
|
||||
review_scope: "Historical decision retained; current factual and authority scope clarified, no new grant"
|
||||
decided_by: Bernd Worsch
|
||||
date: "2026-03-12"
|
||||
tags: ["architecture", "agent-runtime", "llm", "ooda", "bounded-agency"]
|
||||
|
|
@ -18,7 +20,44 @@ tags: ["architecture", "agent-runtime", "llm", "ooda", "bounded-agency"]
|
|||
|
||||
Accepted.
|
||||
|
||||
## Context
|
||||
## Technical currency review — 2026-10-11
|
||||
|
||||
The accepted decision remains the **v0.1 bootstrap architecture for the
|
||||
standalone package under `runtime/`**, not a grant to every coding agent or
|
||||
an assertion that a privileged autonomous runtime is deployed. Current source
|
||||
still implements D1–D5: this package location, a single synchronous CLI cycle,
|
||||
configurable `llm-connect` with `claude-code` default, three explicit HTTP write
|
||||
classes, and a fenced JSON action plan plus a session note. All 32 existing
|
||||
offline runtime tests pass with HTTP calls mocked. That verifies the described
|
||||
prototype behavior; it proves no live authorization, runtime isolation,
|
||||
credential boundary, semantic correctness of task updates, or rollout state.
|
||||
|
||||
D4's original permission is limited to that bootstrap's named low-impact
|
||||
coordination operations. It is neither an approval token nor a Codex autopilot
|
||||
grant, and it does not override owner-source work records, API authorization,
|
||||
work-record lanes or agent-specific supervision. Current governance is
|
||||
`docs/agent-autonomy-decision.md` (founder, 2026-09-28): new scopes start
|
||||
supervised and autopilot requires a separate explicit scoped grant and verified
|
||||
runtime enforcement. The inspected `custodian-codex` supervision record remains
|
||||
supervised, with no autopilot grant and interactive isolation not yet migrated.
|
||||
This review changes no such authority or record.
|
||||
|
||||
The March dependency/deployment statements below are historical context.
|
||||
The review does not reaffirm that every deployment has Claude Code, that the
|
||||
bootstrap is scheduled or deployed, or that its narrow operation list provides
|
||||
credential isolation. API addresses and live change procedures belong to
|
||||
`docs/agent-environment-orientation.md`; task/workplan sources follow ADR-001
|
||||
and the later accepted ADR-012. The code's direct task-status HTTP branch is
|
||||
prototype implementation evidence, not permission to override file-backed
|
||||
owner records. Any widening of D4 or promotion remains a separate decision.
|
||||
|
||||
The original 2026-03-12 decision and accepted-1 bytes are retained in
|
||||
`docs/evidence/2026-10-11-adr-currency-review/adr-002-accepted-1.md`.
|
||||
`accepted-2` is this dated factual/scope annotation, not a new human adoption
|
||||
of an expanded action surface. The detailed source hashes and test receipt
|
||||
are in `docs/evidence/2026-10-11-adr-currency-review/review.json`.
|
||||
|
||||
## Context (historical decision basis)
|
||||
|
||||
CUST-WP-0001 requires a first working skeleton of the Custodian as an acting
|
||||
agent: a loop that observes project state, reasons about it, and executes
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@ type: architecture-decision-record
|
|||
title: "Connectivity-First Network Posture for Custodian Infrastructure"
|
||||
status: accepted
|
||||
owner: the-custodian
|
||||
revision: "accepted-1"
|
||||
last_reviewed: "2026-03-26"
|
||||
revision: "accepted-2"
|
||||
last_reviewed: "2026-10-11"
|
||||
review_interval: 6m
|
||||
reviewed_by: "codex — technical source/currency review"
|
||||
review_scope: "Historical decision retained; current factual and authority scope clarified, no new grant"
|
||||
decided_by: Bernd Worsch
|
||||
date: "2026-03-26"
|
||||
tags: ["architecture", "network", "ops-bridge", "connectivity", "isolation", "security-posture"]
|
||||
|
|
@ -18,7 +20,60 @@ tags: ["architecture", "network", "ops-bridge", "connectivity", "isolation", "se
|
|||
|
||||
Accepted.
|
||||
|
||||
## Context
|
||||
## Technical currency review — 2026-10-11
|
||||
|
||||
The accepted decision remains a **transport-connectivity posture**: needed
|
||||
connections use named, observable, managed paths with deliberate lifecycle
|
||||
continuity. Persistent transport is compatible with independently limited
|
||||
credentials, authenticated callers and resource-side authorization. It grants
|
||||
neither access to every connected component nor authority to bypass isolation
|
||||
required by the current security model. No blanket trust, permanent credential
|
||||
permission or autonomous privileged execution is adopted by this review.
|
||||
|
||||
The March Context, Rationale and deployment consequences below are retained
|
||||
as the original decision's historical basis, not current environment facts or
|
||||
a present security assurance. Specifically:
|
||||
|
||||
- The central State Hub is on railiance01. The workstation address
|
||||
`http://127.0.0.1:8000` is a managed local forward; remote in-cluster callers
|
||||
use `http://10.43.68.154:8000`. The old remote-to-workstation reverse tunnel
|
||||
`http://127.0.0.1:18000` is retired. An optional local relay at :18080 is a
|
||||
separate explicitly enabled route, not the primary hub.
|
||||
- OpsBridge owns observable connectivity, not credentials or authorization.
|
||||
Its INTENT explicitly disclaims credential-authority and security-policy
|
||||
ownership. Its older SCOPE still describes a workstation-local Hub and must
|
||||
not be used as current topology proof. Use the Custodian environment
|
||||
orientation and current owner records before selecting a route.
|
||||
- Observability and a read-model design are **not proof that unauthorized
|
||||
writes, lateral movement or credential exposure are prevented**. The old
|
||||
narrow-write rationale is not a current control assurance. Source authority
|
||||
is governed by accepted ADR-001/ADR-012, and concrete service security is
|
||||
governed by its owners and accepted NetKingdom security-layer v0.8.
|
||||
- SSH encryption protects its transport; it does not guarantee that a
|
||||
forwarded application is authenticated, that listener binding exposes no
|
||||
service, or that credentials cannot reach a process or log. The original
|
||||
“no inbound ports exposed” and age/SOPS phrases are not estate-wide proof.
|
||||
Current custody routing and secret-output rules remain mandatory.
|
||||
- Build mode and absence of customers do not waive tenant boundaries, service
|
||||
admission, credential isolation or exact live-change approval. The
|
||||
agent-specific supervision decision of 2026-09-28 governs privileged
|
||||
execution; the Kubernetes change gate of 2026-09-21 governs its change path.
|
||||
Those existing decisions, not this transport ADR, supply authority.
|
||||
|
||||
These clarifications apply existing accepted governance rather than replacing
|
||||
the connectivity-first decision with a new isolation-first mandate. Changing
|
||||
the transport posture or granting new authority would require a separate
|
||||
proposal and human disposition. No fresh live topology or isolation audit was
|
||||
performed by this source review; environment facts above retain the evidence
|
||||
dates and ownership in `docs/agent-environment-orientation.md`.
|
||||
|
||||
The original 2026-03-26 decision and accepted-1 bytes are retained in
|
||||
`docs/evidence/2026-10-11-adr-currency-review/adr-004-accepted-1.md`.
|
||||
`accepted-2` is this dated factual/scope annotation; the original decision
|
||||
author and date remain history. Detailed source hashes and review findings
|
||||
are in `docs/evidence/2026-10-11-adr-currency-review/review.json`.
|
||||
|
||||
## Context (historical decision basis)
|
||||
|
||||
The Custodian infrastructure spans multiple machines: a primary workstation, a
|
||||
shared compute node (COULOMBCORE), and Railiance cluster nodes. Agents running
|
||||
|
|
|
|||
|
|
@ -0,0 +1,242 @@
|
|||
<!doctype html>
|
||||
<html lang="en"><meta charset="utf-8">
|
||||
<meta name="policy-source-revision" content="4039c9d1c08c92014ecc0a65dda63cc73ba187bb">
|
||||
<meta name="policy-source-digest" content="6aef66cd5cf71a48f5b4e14401dc19a755e2b182445b272d5952df0eb0dea8ec">
|
||||
<title>Custodian Agent Runtime — v0.1 Bootstrap Design</title>
|
||||
<style>
|
||||
:root{
|
||||
--paper:#EDEEF0; --surface:#F6F7F8; --surface-2:#E4E6E9;
|
||||
--ink:#171D24; --ink-2:#4A5561; --ink-3:#737E8A;
|
||||
--rule:#D3D7DC; --rule-strong:#B6BCC3;
|
||||
--brass:#8A6A2E; --brass-soft:#EFE5CD; --brass-line:#C9AE74;
|
||||
--clay:#8A3A2C; --clay-soft:#F2DFDA;
|
||||
--l0:#DCE0E2; --l1:#B9C4C7; --l2:#8CA1A6; --l3:#567D84; --l4:#23555E;
|
||||
--chip-fg:#F6F7F8;
|
||||
--font-display:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",sans-serif;
|
||||
--font-body:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif;
|
||||
--font-mono:ui-monospace,"SF Mono","Cascadia Code",Menlo,Consolas,monospace;
|
||||
--measure:66ch;
|
||||
}
|
||||
@media (prefers-color-scheme:dark){
|
||||
:root:not([data-theme="light"]){
|
||||
--paper:#12161A; --surface:#191E24; --surface-2:#222831;
|
||||
--ink:#E6E9EC; --ink-2:#A3ADB7; --ink-3:#78838E;
|
||||
--rule:#2A3138; --rule-strong:#3B444D;
|
||||
--brass:#C9A45C; --brass-soft:#33290F; --brass-line:#6B5426;
|
||||
--clay:#D08A76; --clay-soft:#3A211B;
|
||||
--l0:#262C32; --l1:#35424A; --l2:#4A626B; --l3:#6A939D; --l4:#97C4CD;
|
||||
--chip-fg:#12161A;
|
||||
}
|
||||
}
|
||||
:root[data-theme="dark"]{
|
||||
--paper:#12161A; --surface:#191E24; --surface-2:#222831;
|
||||
--ink:#E6E9EC; --ink-2:#A3ADB7; --ink-3:#78838E;
|
||||
--rule:#2A3138; --rule-strong:#3B444D;
|
||||
--brass:#C9A45C; --brass-soft:#33290F; --brass-line:#6B5426;
|
||||
--clay:#D08A76; --clay-soft:#3A211B;
|
||||
--l0:#262C32; --l1:#35424A; --l2:#4A626B; --l3:#6A939D; --l4:#97C4CD;
|
||||
--chip-fg:#12161A;
|
||||
}
|
||||
|
||||
*{box-sizing:border-box}
|
||||
body{
|
||||
margin:0; background:var(--paper); color:var(--ink);
|
||||
font-family:var(--font-body); font-size:17px; line-height:1.62;
|
||||
-webkit-font-smoothing:antialiased;
|
||||
}
|
||||
.wrap{max-width:1180px;margin:0 auto;padding:0 24px 96px}
|
||||
.layout{display:grid;grid-template-columns:180px minmax(0,1fr);gap:56px;align-items:start}
|
||||
@media (max-width:960px){.layout{grid-template-columns:1fr;gap:0}.rail{display:none}}
|
||||
|
||||
/* ---------- rail ---------- */
|
||||
.rail{position:sticky;top:28px;padding-top:8px;font-family:var(--font-display);font-size:12px;line-height:1.5}
|
||||
.rail ol{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px}
|
||||
.rail a{color:var(--ink-3);text-decoration:none;display:flex;gap:9px}
|
||||
.rail a:hover,.rail a:focus-visible{color:var(--brass)}
|
||||
.rail .n{font-family:var(--font-mono);font-size:10px;color:var(--rule-strong);min-width:16px;padding-top:1px}
|
||||
.rail .grp{margin-top:14px;font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--rule-strong)}
|
||||
|
||||
/* ---------- header ---------- */
|
||||
header{padding:64px 0 40px;border-bottom:2px solid var(--ink);margin-bottom:44px}
|
||||
.eyebrow{font-family:var(--font-mono);font-size:11.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-3);display:flex;flex-wrap:wrap;gap:14px;margin-bottom:22px}
|
||||
.eyebrow .stat{color:var(--clay)}
|
||||
h1{font-family:var(--font-display);font-weight:800;letter-spacing:-.035em;line-height:.94;font-size:clamp(46px,9vw,92px);margin:0 0 6px;text-wrap:balance}
|
||||
.sub{font-family:var(--font-display);font-weight:500;font-size:clamp(16px,2.4vw,21px);letter-spacing:-.01em;color:var(--ink-2);margin:0 0 30px;max-width:34ch;line-height:1.3}
|
||||
.metagrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:20px 28px;border-top:1px solid var(--rule);padding-top:20px}
|
||||
.metagrid dt{font-family:var(--font-mono);font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-3);margin-bottom:5px}
|
||||
.metagrid dd{margin:0;font-family:var(--font-display);font-size:13.5px;line-height:1.45;color:var(--ink)}
|
||||
|
||||
/* ---------- typography ---------- */
|
||||
section{margin-bottom:60px;scroll-margin-top:24px}
|
||||
h2{font-family:var(--font-display);font-weight:750;letter-spacing:-.022em;font-size:clamp(24px,3.4vw,31px);line-height:1.12;margin:0 0 18px;text-wrap:balance;display:flex;gap:14px;align-items:baseline}
|
||||
h2 .sn{font-family:var(--font-mono);font-size:12px;font-weight:400;color:var(--brass);letter-spacing:.06em;flex:none;padding-top:2px}
|
||||
h3{font-family:var(--font-display);font-weight:700;font-size:16px;letter-spacing:-.008em;margin:34px 0 10px;color:var(--ink)}
|
||||
p{margin:0 0 15px;max-width:var(--measure)}
|
||||
ul,ol{max-width:var(--measure);margin:0 0 15px;padding-left:20px}
|
||||
li{margin-bottom:7px}
|
||||
strong{font-weight:600}
|
||||
em{font-style:italic}
|
||||
code{font-family:var(--font-mono);font-size:.855em;background:var(--surface-2);padding:1px 5px;border-radius:2px}
|
||||
a{color:var(--brass)}
|
||||
.lede{font-size:19px;line-height:1.55;color:var(--ink-2);max-width:60ch}
|
||||
|
||||
/* ---------- devices ---------- */
|
||||
.callout{border-left:3px solid var(--brass);background:var(--brass-soft);padding:18px 22px;margin:0 0 24px;max-width:var(--measure)}
|
||||
.callout p:last-child{margin-bottom:0}
|
||||
.callout .lbl{font-family:var(--font-mono);font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--brass);display:block;margin-bottom:8px}
|
||||
.rule-quote{border-top:2px solid var(--ink);border-bottom:2px solid var(--ink);padding:26px 0;margin:28px 0;max-width:var(--measure)}
|
||||
.rule-quote p{font-family:var(--font-display);font-weight:600;font-size:19px;line-height:1.38;letter-spacing:-.014em;margin:0;text-wrap:balance}
|
||||
.hard{border-left:3px solid var(--clay);background:var(--clay-soft);padding:18px 22px;margin:0 0 24px;max-width:var(--measure)}
|
||||
.hard .lbl{font-family:var(--font-mono);font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--clay);display:block;margin-bottom:8px}
|
||||
.hard p:last-child{margin-bottom:0}
|
||||
.dec{font-family:var(--font-mono);font-size:10.5px;letter-spacing:.08em;color:var(--brass);text-transform:uppercase}
|
||||
.vec{font-family:var(--font-mono);font-size:.9em;font-weight:600;background:var(--surface-2);padding:2px 7px;border-radius:2px;white-space:nowrap;letter-spacing:.04em}
|
||||
|
||||
/* ---------- tables ---------- */
|
||||
.scroll{overflow-x:auto;margin:0 0 24px;-webkit-overflow-scrolling:touch}
|
||||
table{border-collapse:collapse;width:100%;min-width:520px;font-family:var(--font-display);font-size:13.5px;line-height:1.45}
|
||||
th{text-align:left;font-family:var(--font-mono);font-size:9.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-3);font-weight:400;padding:0 16px 8px 0;border-bottom:1px solid var(--rule-strong);vertical-align:bottom}
|
||||
td{padding:11px 16px 11px 0;border-bottom:1px solid var(--rule);vertical-align:top;color:var(--ink-2)}
|
||||
td:first-child{color:var(--ink);font-weight:600}
|
||||
tbody tr:last-child td{border-bottom:none}
|
||||
.lvl{font-family:var(--font-mono);font-weight:600;font-size:12px;letter-spacing:.04em;color:var(--ink)}
|
||||
|
||||
/* ---------- ladders ---------- */
|
||||
.breakout{margin:34px 0 40px}
|
||||
.bhead{display:flex;justify-content:space-between;align-items:baseline;gap:20px;border-bottom:1px solid var(--rule-strong);padding-bottom:9px;margin-bottom:22px;flex-wrap:wrap}
|
||||
.bhead h3{margin:0;font-size:13px;letter-spacing:.1em;text-transform:uppercase;font-family:var(--font-mono);font-weight:400;color:var(--ink-3)}
|
||||
.bhead .note{font-family:var(--font-display);font-size:12.5px;color:var(--ink-3)}
|
||||
.ladders{display:grid;gap:26px}
|
||||
.ladder{display:grid;grid-template-columns:126px minmax(0,1fr);gap:18px;align-items:start}
|
||||
@media (max-width:700px){.ladder{grid-template-columns:1fr;gap:10px}}
|
||||
.ladder .pname{font-family:var(--font-display);font-weight:700;font-size:14px;letter-spacing:-.01em;padding-top:2px}
|
||||
.ladder .pname span{display:block;font-family:var(--font-mono);font-size:10px;font-weight:400;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);margin-top:3px}
|
||||
.rungs{display:grid;gap:3px;grid-template-columns:repeat(5,minmax(0,1fr))}
|
||||
@media (max-width:700px){.rungs{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
||||
.rung{padding:9px 10px 11px;background:var(--surface);border-top:4px solid var(--l0);min-width:0}
|
||||
.rung.r1{border-top-color:var(--l1)} .rung.r2{border-top-color:var(--l2)}
|
||||
.rung.r3{border-top-color:var(--l3)} .rung.r4{border-top-color:var(--l4)}
|
||||
.rung .code{font-family:var(--font-mono);font-size:11px;font-weight:600;letter-spacing:.06em;color:var(--ink);display:block;margin-bottom:4px}
|
||||
.rung .txt{font-family:var(--font-display);font-size:11.5px;line-height:1.34;color:var(--ink-2);display:block}
|
||||
.rung.na{opacity:.42}
|
||||
|
||||
/* ---------- matrix ---------- */
|
||||
.matrix-shell{display:grid;grid-template-columns:auto minmax(0,1fr);gap:12px;align-items:stretch;margin-bottom:14px}
|
||||
.ylab{writing-mode:vertical-rl;transform:rotate(180deg);font-family:var(--font-mono);font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-3);text-align:center;padding-bottom:22px}
|
||||
.mgrid{display:grid;grid-template-columns:34px repeat(5,minmax(0,1fr));gap:3px}
|
||||
.mcell{background:var(--surface);min-height:60px;padding:6px;display:flex;flex-direction:column;justify-content:flex-end;gap:4px;min-width:0}
|
||||
.mcell.tint1{background:color-mix(in srgb,var(--l1) 26%,var(--surface))}
|
||||
.mcell.tint2{background:color-mix(in srgb,var(--l2) 26%,var(--surface))}
|
||||
.mcell.tint3{background:color-mix(in srgb,var(--l3) 24%,var(--surface))}
|
||||
.mcell.tint4{background:color-mix(in srgb,var(--l4) 22%,var(--surface))}
|
||||
.mcell.void{background:repeating-linear-gradient(135deg,transparent,transparent 5px,var(--rule) 5px,var(--rule) 6px);opacity:.55}
|
||||
.rlab,.clab{font-family:var(--font-mono);font-size:10px;font-weight:600;letter-spacing:.05em;color:var(--ink-3);display:flex;align-items:center;justify-content:center}
|
||||
.rlab{min-height:60px}
|
||||
.clab{padding-top:7px;min-height:22px}
|
||||
.pin{font-family:var(--font-mono);font-size:9.5px;font-weight:600;letter-spacing:.02em;background:var(--ink);color:var(--paper);padding:2px 5px;border-radius:2px;line-height:1.3;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.pin.ghost{background:transparent;color:var(--ink-2);border:1px dashed var(--rule-strong)}
|
||||
.mnote{display:flex;gap:22px;flex-wrap:wrap;font-family:var(--font-display);font-size:12px;color:var(--ink-3);padding-top:6px}
|
||||
.mnote .k{display:flex;align-items:center;gap:7px}
|
||||
.sw{width:13px;height:13px;flex:none;background:var(--ink)}
|
||||
.sw.g{background:transparent;border:1px dashed var(--rule-strong)}
|
||||
.sw.v{background:repeating-linear-gradient(135deg,transparent,transparent 4px,var(--rule) 4px,var(--rule) 5px);border:1px solid var(--rule)}
|
||||
@media (max-width:640px){
|
||||
.mgrid{grid-template-columns:28px repeat(5,minmax(0,1fr))}
|
||||
.mcell{min-height:52px;padding:4px}
|
||||
.pin{font-size:8px;padding:1px 3px}
|
||||
.rlab{min-height:52px}
|
||||
}
|
||||
|
||||
/* ---------- methodology ---------- */
|
||||
.verbs{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:2px;background:var(--rule);border:1px solid var(--rule)}
|
||||
.verb{background:var(--surface);padding:18px 18px 20px}
|
||||
.verb h4{font-family:var(--font-display);font-weight:750;font-size:15px;margin:0 0 7px;letter-spacing:-.01em}
|
||||
.verb p{font-family:var(--font-display);font-size:12.5px;line-height:1.46;color:var(--ink-2);margin:0;max-width:none}
|
||||
.verb .step{font-family:var(--font-mono);font-size:9.5px;letter-spacing:.13em;color:var(--brass);display:block;margin-bottom:9px}
|
||||
|
||||
/* ---------- questions ---------- */
|
||||
.qs{display:flex;flex-direction:column;gap:0;border-top:1px solid var(--rule-strong)}
|
||||
.q{display:grid;grid-template-columns:34px minmax(0,1fr) 170px;gap:18px;padding:16px 0;border-bottom:1px solid var(--rule);align-items:start}
|
||||
@media (max-width:760px){.q{grid-template-columns:28px minmax(0,1fr);gap:12px}.q .owner{grid-column:2}}
|
||||
.q .qn{font-family:var(--font-mono);font-size:11px;color:var(--brass);padding-top:3px}
|
||||
.q .qt{font-family:var(--font-display);font-size:14px;line-height:1.48;color:var(--ink-2)}
|
||||
.q .qt b{color:var(--ink);font-weight:700;display:block;margin-bottom:2px;font-size:14.5px}
|
||||
.owner{font-family:var(--font-mono);font-size:10px;letter-spacing:.05em;color:var(--ink-3);padding-top:4px}
|
||||
.owner .tag{display:inline-block;border:1px solid var(--rule-strong);padding:2px 7px;border-radius:2px}
|
||||
.owner .tag.need{border-color:var(--clay);color:var(--clay)}
|
||||
|
||||
/* ---------- misc ---------- */
|
||||
.numbers{font-family:var(--font-mono);font-size:12.5px;line-height:1.85;background:var(--surface);border-left:3px solid var(--l3);padding:16px 20px;margin:0 0 22px;overflow-x:auto;max-width:var(--measure)}
|
||||
.numbers .v{color:var(--ink);font-weight:600}
|
||||
.numbers .k{color:var(--ink-3)}
|
||||
pre{font-family:var(--font-mono);font-size:12.5px;line-height:1.68;background:var(--surface);border-left:3px solid var(--rule-strong);padding:16px 20px;overflow-x:auto;margin:0 0 22px;max-width:var(--measure);color:var(--ink-2)}
|
||||
.alt{border-bottom:1px solid var(--rule);padding:14px 0;max-width:var(--measure)}
|
||||
.alt:last-of-type{border-bottom:none}
|
||||
.alt b{font-family:var(--font-display);font-size:14px;display:block;margin-bottom:3px}
|
||||
.alt p{font-size:14.5px;margin:0;color:var(--ink-2)}
|
||||
.alt .verdict{font-family:var(--font-mono);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--clay)}
|
||||
footer{border-top:2px solid var(--ink);margin-top:20px;padding-top:22px;font-family:var(--font-mono);font-size:11px;letter-spacing:.06em;color:var(--ink-3);display:flex;justify-content:space-between;gap:20px;flex-wrap:wrap}
|
||||
.tm td,.tm th{text-align:center}
|
||||
.tm td:first-child,.tm th:first-child{text-align:left}
|
||||
.yes{color:var(--l4);font-weight:700}
|
||||
.no{color:var(--clay);font-weight:700}
|
||||
.kind{font-family:var(--font-mono);font-size:9px;letter-spacing:.09em;text-transform:uppercase;padding:2px 6px;border-radius:2px;white-space:nowrap;border:1px solid var(--rule-strong);color:var(--ink-3)}
|
||||
.kind.adv{border-color:var(--clay);color:var(--clay)}
|
||||
.routes{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:2px;background:var(--rule);border:1px solid var(--rule);margin:0 0 22px}
|
||||
.route{background:var(--surface);padding:16px 18px}
|
||||
.route h4{font-family:var(--font-display);font-weight:750;font-size:14px;margin:0 0 6px}
|
||||
.route p{font-family:var(--font-display);font-size:12.5px;line-height:1.45;color:var(--ink-2);margin:0;max-width:none}
|
||||
.route .tag{font-family:var(--font-mono);font-size:9px;letter-spacing:.1em;text-transform:uppercase;color:var(--brass);display:block;margin-bottom:8px}
|
||||
a:focus-visible,.rail a:focus-visible{outline:2px solid var(--brass);outline-offset:3px}
|
||||
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
|
||||
|
||||
</style>
|
||||
<div class="wrap"><header><div class="eyebrow"><span>CUST-ADR-002</span> <span class="stat">accepted · accepted-1</span> <span>the-custodian</span> <span>reviewed 2026-03-12</span><span>generated from canonical source — do not edit</span></div><h1>Custodian Agent Runtime — v0.1 Bootstrap Design</h1><p class="sub">Source: <code>the-custodian · canon/architecture/adr-002-custodian-agent-runtime-design.md · 4039c9d1c08c92014ecc0a65dda63cc73ba187bb</code></p><p class="sub">Review due: 2026-09-12</p></header><div class="layout"><nav class="rail" aria-label="Sections"><ol><li><a href="#status"><span class="n">·</span>Status</a></li><li><a href="#context"><span class="n">·</span>Context</a></li><li><a href="#decisions"><span class="n">·</span>Decisions</a></li><li><a href="#consequences"><span class="n">·</span>Consequences</a></li><li><a href="#deferred"><span class="n">·</span>Deferred</a></li></ol></nav><main><section id="status"><h2>Status</h2>
|
||||
<p>Accepted.</p>
|
||||
</section>
|
||||
<section id="context"><h2>Context</h2>
|
||||
<p>CUST-WP-0001 requires a first working skeleton of the Custodian as an acting agent: a loop that observes project state, reasons about it, and executes bounded write operations — without human interaction for each step.</p>
|
||||
<p>The dependencies (llm-connect, operational Railiance infra) are now resolved. This ADR captures the five key architectural decisions for the v0.1 bootstrap.</p>
|
||||
</section>
|
||||
<section id="decisions"><h2>Decisions</h2>
|
||||
<h3>D1 — Location: <code>runtime/</code> inside the-custodian (not a new repo)</h3>
|
||||
<p><strong>Decision:</strong> The runtime lives under <code>the-custodian/runtime/</code> as a standalone Python package (<code>pyproject.toml</code>, own venv) rather than a new repository.</p>
|
||||
<p><strong>Rationale:</strong> The runtime is tightly coupled to canon (reads constitution, memory) and the state-hub (its primary coordination layer). A separate repo adds friction with no v0.1 benefit. The existing <code>runtime/</code> scaffold confirms the original intent. Extraction to its own repo is deferred to when the runtime has stable boundaries and multiple consumers.</p>
|
||||
<h3>D2 — OODA loop: single-pass synchronous CLI</h3>
|
||||
<p><strong>Decision:</strong> One <code>run()</code> call = one complete Observe → Orient → Decide → Act cycle. Entry point is a CLI (<code>agent.py</code>) invoked manually or by a cron job.</p>
|
||||
<pre>Observe — HTTP GET to state-hub: state summary or domain summary
|
||||
Orient — Load constitution + build structured LLM context prompt
|
||||
Decide — Single LLM call (via llm-connect) returns a JSON action plan
|
||||
Act — Execute only sanctioned write operations from the plan</pre>
|
||||
<p><strong>Rationale:</strong> Async event loops and daemons add operational complexity that v0.1 doesn't need. A single-pass CLI is testable, debuggable, and can be scheduled externally. The transition to an event-driven loop is Phase 2.</p>
|
||||
<h3>D3 — LLM backend: ClaudeCodeAdapter by default</h3>
|
||||
<p><strong>Decision:</strong> The runtime uses <code>llm_connect.ClaudeCodeAdapter</code> as its default LLM backend (shells out to <code>claude --print</code>). Provider is configurable via <code>--llm</code> flag to support <code>gemini</code>, <code>openrouter</code>, or <code>openai</code>.</p>
|
||||
<p><strong>Rationale:</strong> <code>ClaudeCodeAdapter</code> requires no API key and honours the Local-First value (V2). All current deployments have Claude Code available. The llm-connect abstraction means switching providers is a one-line change.</p>
|
||||
<h3>D4 — Action constraint: three sanctioned write operations only</h3>
|
||||
<p><strong>Decision:</strong> The agent may execute exactly three state-hub write operations without human approval:</p>
|
||||
<ol><li><code>add_progress_event</code> — append an observation to the event log</li><li><code>update_task_status</code> — mark a task done/in_progress (reversible)</li><li><code>flag_for_human</code> — raise an intervention flag (escalation, not action)</li></ol>
|
||||
<p>All other operations (create workstream, record decision, resolve decision, write to canon) require human approval before execution.</p>
|
||||
<p><strong>Rationale:</strong> Constitution §3/§4 require bounded agency. The three operations are either append-only (progress events), reversible (task status), or explicitly escalating (flag). They cannot produce irreversible harm.</p>
|
||||
<h3>D5 — LLM response format: JSON block in markdown</h3>
|
||||
<p><strong>Decision:</strong> The LLM is prompted to return a Markdown response with a fenced ```json block containing the structured action plan:</p>
|
||||
<pre>{
|
||||
"observations": ["..."],
|
||||
"progress_events": [
|
||||
{"summary": "...", "workstream_id": "...", "event_type": "note"}
|
||||
],
|
||||
"tasks_to_update": [
|
||||
{"task_id": "...", "status": "done"}
|
||||
],
|
||||
"tasks_to_flag": [
|
||||
{"task_id": "...", "note": "..."}
|
||||
]
|
||||
}</pre>
|
||||
<p>The surrounding Markdown is preserved as a human-readable reasoning trace and written to <code>memory/working/</code> as a session note.</p>
|
||||
<p><strong>Rationale:</strong> JSON blocks are robust to extraction (delimited), LLMs produce them reliably with clear instructions, and the surrounding prose gives Bernd an auditable reasoning trace without requiring a separate reasoning step.</p>
|
||||
</section>
|
||||
<section id="consequences"><h2>Consequences</h2>
|
||||
<ul><li><code>runtime/</code> becomes a standalone Python package; <code>make agent-run DOMAIN=x</code> invokes it.</li><li>The runtime has no DB schema changes and no new API endpoints — it is a pure client of the existing state-hub HTTP API.</li><li>Autonomous actions are limited to append-only writes and escalations. Any expansion of the action surface requires a new ADR and human approval.</li><li>The v0.1 loop is single-user (Bernd). Multi-agent expansion is Phase 2+.</li></ul>
|
||||
</section>
|
||||
<section id="deferred"><h2>Deferred</h2>
|
||||
<ul><li>Async event loop / daemon mode (Phase 2)</li><li>RAG over canon (Phase 1 roadmap item)</li><li>Tool adapters beyond state-hub HTTP (planned in <code>runtime/tool_adapters/</code>)</li><li>Deployment on Railiance k3s as a scheduled CronJob</li></ul>
|
||||
</section><footer><span>CUST-ADR-002 · accepted-1 · accepted</span><span>the-custodian · canon/architecture/adr-002-custodian-agent-runtime-design.md · 4039c9d1c08c92014ecc0a65dda63cc73ba187bb</span></footer></main></div></div></html>
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
---
|
||||
id: CUST-ADR-002
|
||||
type: architecture-decision-record
|
||||
title: "Custodian Agent Runtime — v0.1 Bootstrap Design"
|
||||
status: accepted
|
||||
owner: the-custodian
|
||||
revision: "accepted-1"
|
||||
last_reviewed: "2026-03-12"
|
||||
review_interval: 6m
|
||||
decided_by: Bernd Worsch
|
||||
date: "2026-03-12"
|
||||
tags: ["architecture", "agent-runtime", "llm", "ooda", "bounded-agency"]
|
||||
---
|
||||
|
||||
# ADR-002: Custodian Agent Runtime — v0.1 Bootstrap Design
|
||||
|
||||
## Status
|
||||
|
||||
Accepted.
|
||||
|
||||
## Context
|
||||
|
||||
CUST-WP-0001 requires a first working skeleton of the Custodian as an acting
|
||||
agent: a loop that observes project state, reasons about it, and executes
|
||||
bounded write operations — without human interaction for each step.
|
||||
|
||||
The dependencies (llm-connect, operational Railiance infra) are now resolved.
|
||||
This ADR captures the five key architectural decisions for the v0.1 bootstrap.
|
||||
|
||||
## Decisions
|
||||
|
||||
### D1 — Location: `runtime/` inside the-custodian (not a new repo)
|
||||
|
||||
**Decision:** The runtime lives under `the-custodian/runtime/` as a standalone
|
||||
Python package (`pyproject.toml`, own venv) rather than a new repository.
|
||||
|
||||
**Rationale:** The runtime is tightly coupled to canon (reads constitution,
|
||||
memory) and the state-hub (its primary coordination layer). A separate repo
|
||||
adds friction with no v0.1 benefit. The existing `runtime/` scaffold confirms
|
||||
the original intent. Extraction to its own repo is deferred to when the
|
||||
runtime has stable boundaries and multiple consumers.
|
||||
|
||||
### D2 — OODA loop: single-pass synchronous CLI
|
||||
|
||||
**Decision:** One `run()` call = one complete Observe → Orient → Decide → Act
|
||||
cycle. Entry point is a CLI (`agent.py`) invoked manually or by a cron job.
|
||||
|
||||
```
|
||||
Observe — HTTP GET to state-hub: state summary or domain summary
|
||||
Orient — Load constitution + build structured LLM context prompt
|
||||
Decide — Single LLM call (via llm-connect) returns a JSON action plan
|
||||
Act — Execute only sanctioned write operations from the plan
|
||||
```
|
||||
|
||||
**Rationale:** Async event loops and daemons add operational complexity that
|
||||
v0.1 doesn't need. A single-pass CLI is testable, debuggable, and can be
|
||||
scheduled externally. The transition to an event-driven loop is Phase 2.
|
||||
|
||||
### D3 — LLM backend: ClaudeCodeAdapter by default
|
||||
|
||||
**Decision:** The runtime uses `llm_connect.ClaudeCodeAdapter` as its default
|
||||
LLM backend (shells out to `claude --print`). Provider is configurable via
|
||||
`--llm` flag to support `gemini`, `openrouter`, or `openai`.
|
||||
|
||||
**Rationale:** `ClaudeCodeAdapter` requires no API key and honours the
|
||||
Local-First value (V2). All current deployments have Claude Code available.
|
||||
The llm-connect abstraction means switching providers is a one-line change.
|
||||
|
||||
### D4 — Action constraint: three sanctioned write operations only
|
||||
|
||||
**Decision:** The agent may execute exactly three state-hub write operations
|
||||
without human approval:
|
||||
1. `add_progress_event` — append an observation to the event log
|
||||
2. `update_task_status` — mark a task done/in_progress (reversible)
|
||||
3. `flag_for_human` — raise an intervention flag (escalation, not action)
|
||||
|
||||
All other operations (create workstream, record decision, resolve decision,
|
||||
write to canon) require human approval before execution.
|
||||
|
||||
**Rationale:** Constitution §3/§4 require bounded agency. The three operations
|
||||
are either append-only (progress events), reversible (task status), or
|
||||
explicitly escalating (flag). They cannot produce irreversible harm.
|
||||
|
||||
### D5 — LLM response format: JSON block in markdown
|
||||
|
||||
**Decision:** The LLM is prompted to return a Markdown response with a
|
||||
fenced ```json block containing the structured action plan:
|
||||
|
||||
```json
|
||||
{
|
||||
"observations": ["..."],
|
||||
"progress_events": [
|
||||
{"summary": "...", "workstream_id": "...", "event_type": "note"}
|
||||
],
|
||||
"tasks_to_update": [
|
||||
{"task_id": "...", "status": "done"}
|
||||
],
|
||||
"tasks_to_flag": [
|
||||
{"task_id": "...", "note": "..."}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The surrounding Markdown is preserved as a human-readable reasoning trace
|
||||
and written to `memory/working/` as a session note.
|
||||
|
||||
**Rationale:** JSON blocks are robust to extraction (delimited), LLMs produce
|
||||
them reliably with clear instructions, and the surrounding prose gives Bernd
|
||||
an auditable reasoning trace without requiring a separate reasoning step.
|
||||
|
||||
## Consequences
|
||||
|
||||
- `runtime/` becomes a standalone Python package; `make agent-run DOMAIN=x`
|
||||
invokes it.
|
||||
- The runtime has no DB schema changes and no new API endpoints — it is a
|
||||
pure client of the existing state-hub HTTP API.
|
||||
- Autonomous actions are limited to append-only writes and escalations. Any
|
||||
expansion of the action surface requires a new ADR and human approval.
|
||||
- The v0.1 loop is single-user (Bernd). Multi-agent expansion is Phase 2+.
|
||||
|
||||
## Deferred
|
||||
|
||||
- Async event loop / daemon mode (Phase 2)
|
||||
- RAG over canon (Phase 1 roadmap item)
|
||||
- Tool adapters beyond state-hub HTTP (planned in `runtime/tool_adapters/`)
|
||||
- Deployment on Railiance k3s as a scheduled CronJob
|
||||
|
|
@ -0,0 +1,236 @@
|
|||
<!doctype html>
|
||||
<html lang="en"><meta charset="utf-8">
|
||||
<meta name="policy-source-revision" content="4039c9d1c08c92014ecc0a65dda63cc73ba187bb">
|
||||
<meta name="policy-source-digest" content="3b68adfa6ab329e73f857cf691dc405136d2d66a0135e2c37c236aabe4659557">
|
||||
<title>Connectivity-First Network Posture for Custodian Infrastructure</title>
|
||||
<style>
|
||||
:root{
|
||||
--paper:#EDEEF0; --surface:#F6F7F8; --surface-2:#E4E6E9;
|
||||
--ink:#171D24; --ink-2:#4A5561; --ink-3:#737E8A;
|
||||
--rule:#D3D7DC; --rule-strong:#B6BCC3;
|
||||
--brass:#8A6A2E; --brass-soft:#EFE5CD; --brass-line:#C9AE74;
|
||||
--clay:#8A3A2C; --clay-soft:#F2DFDA;
|
||||
--l0:#DCE0E2; --l1:#B9C4C7; --l2:#8CA1A6; --l3:#567D84; --l4:#23555E;
|
||||
--chip-fg:#F6F7F8;
|
||||
--font-display:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",sans-serif;
|
||||
--font-body:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif;
|
||||
--font-mono:ui-monospace,"SF Mono","Cascadia Code",Menlo,Consolas,monospace;
|
||||
--measure:66ch;
|
||||
}
|
||||
@media (prefers-color-scheme:dark){
|
||||
:root:not([data-theme="light"]){
|
||||
--paper:#12161A; --surface:#191E24; --surface-2:#222831;
|
||||
--ink:#E6E9EC; --ink-2:#A3ADB7; --ink-3:#78838E;
|
||||
--rule:#2A3138; --rule-strong:#3B444D;
|
||||
--brass:#C9A45C; --brass-soft:#33290F; --brass-line:#6B5426;
|
||||
--clay:#D08A76; --clay-soft:#3A211B;
|
||||
--l0:#262C32; --l1:#35424A; --l2:#4A626B; --l3:#6A939D; --l4:#97C4CD;
|
||||
--chip-fg:#12161A;
|
||||
}
|
||||
}
|
||||
:root[data-theme="dark"]{
|
||||
--paper:#12161A; --surface:#191E24; --surface-2:#222831;
|
||||
--ink:#E6E9EC; --ink-2:#A3ADB7; --ink-3:#78838E;
|
||||
--rule:#2A3138; --rule-strong:#3B444D;
|
||||
--brass:#C9A45C; --brass-soft:#33290F; --brass-line:#6B5426;
|
||||
--clay:#D08A76; --clay-soft:#3A211B;
|
||||
--l0:#262C32; --l1:#35424A; --l2:#4A626B; --l3:#6A939D; --l4:#97C4CD;
|
||||
--chip-fg:#12161A;
|
||||
}
|
||||
|
||||
*{box-sizing:border-box}
|
||||
body{
|
||||
margin:0; background:var(--paper); color:var(--ink);
|
||||
font-family:var(--font-body); font-size:17px; line-height:1.62;
|
||||
-webkit-font-smoothing:antialiased;
|
||||
}
|
||||
.wrap{max-width:1180px;margin:0 auto;padding:0 24px 96px}
|
||||
.layout{display:grid;grid-template-columns:180px minmax(0,1fr);gap:56px;align-items:start}
|
||||
@media (max-width:960px){.layout{grid-template-columns:1fr;gap:0}.rail{display:none}}
|
||||
|
||||
/* ---------- rail ---------- */
|
||||
.rail{position:sticky;top:28px;padding-top:8px;font-family:var(--font-display);font-size:12px;line-height:1.5}
|
||||
.rail ol{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px}
|
||||
.rail a{color:var(--ink-3);text-decoration:none;display:flex;gap:9px}
|
||||
.rail a:hover,.rail a:focus-visible{color:var(--brass)}
|
||||
.rail .n{font-family:var(--font-mono);font-size:10px;color:var(--rule-strong);min-width:16px;padding-top:1px}
|
||||
.rail .grp{margin-top:14px;font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--rule-strong)}
|
||||
|
||||
/* ---------- header ---------- */
|
||||
header{padding:64px 0 40px;border-bottom:2px solid var(--ink);margin-bottom:44px}
|
||||
.eyebrow{font-family:var(--font-mono);font-size:11.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-3);display:flex;flex-wrap:wrap;gap:14px;margin-bottom:22px}
|
||||
.eyebrow .stat{color:var(--clay)}
|
||||
h1{font-family:var(--font-display);font-weight:800;letter-spacing:-.035em;line-height:.94;font-size:clamp(46px,9vw,92px);margin:0 0 6px;text-wrap:balance}
|
||||
.sub{font-family:var(--font-display);font-weight:500;font-size:clamp(16px,2.4vw,21px);letter-spacing:-.01em;color:var(--ink-2);margin:0 0 30px;max-width:34ch;line-height:1.3}
|
||||
.metagrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:20px 28px;border-top:1px solid var(--rule);padding-top:20px}
|
||||
.metagrid dt{font-family:var(--font-mono);font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-3);margin-bottom:5px}
|
||||
.metagrid dd{margin:0;font-family:var(--font-display);font-size:13.5px;line-height:1.45;color:var(--ink)}
|
||||
|
||||
/* ---------- typography ---------- */
|
||||
section{margin-bottom:60px;scroll-margin-top:24px}
|
||||
h2{font-family:var(--font-display);font-weight:750;letter-spacing:-.022em;font-size:clamp(24px,3.4vw,31px);line-height:1.12;margin:0 0 18px;text-wrap:balance;display:flex;gap:14px;align-items:baseline}
|
||||
h2 .sn{font-family:var(--font-mono);font-size:12px;font-weight:400;color:var(--brass);letter-spacing:.06em;flex:none;padding-top:2px}
|
||||
h3{font-family:var(--font-display);font-weight:700;font-size:16px;letter-spacing:-.008em;margin:34px 0 10px;color:var(--ink)}
|
||||
p{margin:0 0 15px;max-width:var(--measure)}
|
||||
ul,ol{max-width:var(--measure);margin:0 0 15px;padding-left:20px}
|
||||
li{margin-bottom:7px}
|
||||
strong{font-weight:600}
|
||||
em{font-style:italic}
|
||||
code{font-family:var(--font-mono);font-size:.855em;background:var(--surface-2);padding:1px 5px;border-radius:2px}
|
||||
a{color:var(--brass)}
|
||||
.lede{font-size:19px;line-height:1.55;color:var(--ink-2);max-width:60ch}
|
||||
|
||||
/* ---------- devices ---------- */
|
||||
.callout{border-left:3px solid var(--brass);background:var(--brass-soft);padding:18px 22px;margin:0 0 24px;max-width:var(--measure)}
|
||||
.callout p:last-child{margin-bottom:0}
|
||||
.callout .lbl{font-family:var(--font-mono);font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--brass);display:block;margin-bottom:8px}
|
||||
.rule-quote{border-top:2px solid var(--ink);border-bottom:2px solid var(--ink);padding:26px 0;margin:28px 0;max-width:var(--measure)}
|
||||
.rule-quote p{font-family:var(--font-display);font-weight:600;font-size:19px;line-height:1.38;letter-spacing:-.014em;margin:0;text-wrap:balance}
|
||||
.hard{border-left:3px solid var(--clay);background:var(--clay-soft);padding:18px 22px;margin:0 0 24px;max-width:var(--measure)}
|
||||
.hard .lbl{font-family:var(--font-mono);font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--clay);display:block;margin-bottom:8px}
|
||||
.hard p:last-child{margin-bottom:0}
|
||||
.dec{font-family:var(--font-mono);font-size:10.5px;letter-spacing:.08em;color:var(--brass);text-transform:uppercase}
|
||||
.vec{font-family:var(--font-mono);font-size:.9em;font-weight:600;background:var(--surface-2);padding:2px 7px;border-radius:2px;white-space:nowrap;letter-spacing:.04em}
|
||||
|
||||
/* ---------- tables ---------- */
|
||||
.scroll{overflow-x:auto;margin:0 0 24px;-webkit-overflow-scrolling:touch}
|
||||
table{border-collapse:collapse;width:100%;min-width:520px;font-family:var(--font-display);font-size:13.5px;line-height:1.45}
|
||||
th{text-align:left;font-family:var(--font-mono);font-size:9.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-3);font-weight:400;padding:0 16px 8px 0;border-bottom:1px solid var(--rule-strong);vertical-align:bottom}
|
||||
td{padding:11px 16px 11px 0;border-bottom:1px solid var(--rule);vertical-align:top;color:var(--ink-2)}
|
||||
td:first-child{color:var(--ink);font-weight:600}
|
||||
tbody tr:last-child td{border-bottom:none}
|
||||
.lvl{font-family:var(--font-mono);font-weight:600;font-size:12px;letter-spacing:.04em;color:var(--ink)}
|
||||
|
||||
/* ---------- ladders ---------- */
|
||||
.breakout{margin:34px 0 40px}
|
||||
.bhead{display:flex;justify-content:space-between;align-items:baseline;gap:20px;border-bottom:1px solid var(--rule-strong);padding-bottom:9px;margin-bottom:22px;flex-wrap:wrap}
|
||||
.bhead h3{margin:0;font-size:13px;letter-spacing:.1em;text-transform:uppercase;font-family:var(--font-mono);font-weight:400;color:var(--ink-3)}
|
||||
.bhead .note{font-family:var(--font-display);font-size:12.5px;color:var(--ink-3)}
|
||||
.ladders{display:grid;gap:26px}
|
||||
.ladder{display:grid;grid-template-columns:126px minmax(0,1fr);gap:18px;align-items:start}
|
||||
@media (max-width:700px){.ladder{grid-template-columns:1fr;gap:10px}}
|
||||
.ladder .pname{font-family:var(--font-display);font-weight:700;font-size:14px;letter-spacing:-.01em;padding-top:2px}
|
||||
.ladder .pname span{display:block;font-family:var(--font-mono);font-size:10px;font-weight:400;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);margin-top:3px}
|
||||
.rungs{display:grid;gap:3px;grid-template-columns:repeat(5,minmax(0,1fr))}
|
||||
@media (max-width:700px){.rungs{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
||||
.rung{padding:9px 10px 11px;background:var(--surface);border-top:4px solid var(--l0);min-width:0}
|
||||
.rung.r1{border-top-color:var(--l1)} .rung.r2{border-top-color:var(--l2)}
|
||||
.rung.r3{border-top-color:var(--l3)} .rung.r4{border-top-color:var(--l4)}
|
||||
.rung .code{font-family:var(--font-mono);font-size:11px;font-weight:600;letter-spacing:.06em;color:var(--ink);display:block;margin-bottom:4px}
|
||||
.rung .txt{font-family:var(--font-display);font-size:11.5px;line-height:1.34;color:var(--ink-2);display:block}
|
||||
.rung.na{opacity:.42}
|
||||
|
||||
/* ---------- matrix ---------- */
|
||||
.matrix-shell{display:grid;grid-template-columns:auto minmax(0,1fr);gap:12px;align-items:stretch;margin-bottom:14px}
|
||||
.ylab{writing-mode:vertical-rl;transform:rotate(180deg);font-family:var(--font-mono);font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-3);text-align:center;padding-bottom:22px}
|
||||
.mgrid{display:grid;grid-template-columns:34px repeat(5,minmax(0,1fr));gap:3px}
|
||||
.mcell{background:var(--surface);min-height:60px;padding:6px;display:flex;flex-direction:column;justify-content:flex-end;gap:4px;min-width:0}
|
||||
.mcell.tint1{background:color-mix(in srgb,var(--l1) 26%,var(--surface))}
|
||||
.mcell.tint2{background:color-mix(in srgb,var(--l2) 26%,var(--surface))}
|
||||
.mcell.tint3{background:color-mix(in srgb,var(--l3) 24%,var(--surface))}
|
||||
.mcell.tint4{background:color-mix(in srgb,var(--l4) 22%,var(--surface))}
|
||||
.mcell.void{background:repeating-linear-gradient(135deg,transparent,transparent 5px,var(--rule) 5px,var(--rule) 6px);opacity:.55}
|
||||
.rlab,.clab{font-family:var(--font-mono);font-size:10px;font-weight:600;letter-spacing:.05em;color:var(--ink-3);display:flex;align-items:center;justify-content:center}
|
||||
.rlab{min-height:60px}
|
||||
.clab{padding-top:7px;min-height:22px}
|
||||
.pin{font-family:var(--font-mono);font-size:9.5px;font-weight:600;letter-spacing:.02em;background:var(--ink);color:var(--paper);padding:2px 5px;border-radius:2px;line-height:1.3;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.pin.ghost{background:transparent;color:var(--ink-2);border:1px dashed var(--rule-strong)}
|
||||
.mnote{display:flex;gap:22px;flex-wrap:wrap;font-family:var(--font-display);font-size:12px;color:var(--ink-3);padding-top:6px}
|
||||
.mnote .k{display:flex;align-items:center;gap:7px}
|
||||
.sw{width:13px;height:13px;flex:none;background:var(--ink)}
|
||||
.sw.g{background:transparent;border:1px dashed var(--rule-strong)}
|
||||
.sw.v{background:repeating-linear-gradient(135deg,transparent,transparent 4px,var(--rule) 4px,var(--rule) 5px);border:1px solid var(--rule)}
|
||||
@media (max-width:640px){
|
||||
.mgrid{grid-template-columns:28px repeat(5,minmax(0,1fr))}
|
||||
.mcell{min-height:52px;padding:4px}
|
||||
.pin{font-size:8px;padding:1px 3px}
|
||||
.rlab{min-height:52px}
|
||||
}
|
||||
|
||||
/* ---------- methodology ---------- */
|
||||
.verbs{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:2px;background:var(--rule);border:1px solid var(--rule)}
|
||||
.verb{background:var(--surface);padding:18px 18px 20px}
|
||||
.verb h4{font-family:var(--font-display);font-weight:750;font-size:15px;margin:0 0 7px;letter-spacing:-.01em}
|
||||
.verb p{font-family:var(--font-display);font-size:12.5px;line-height:1.46;color:var(--ink-2);margin:0;max-width:none}
|
||||
.verb .step{font-family:var(--font-mono);font-size:9.5px;letter-spacing:.13em;color:var(--brass);display:block;margin-bottom:9px}
|
||||
|
||||
/* ---------- questions ---------- */
|
||||
.qs{display:flex;flex-direction:column;gap:0;border-top:1px solid var(--rule-strong)}
|
||||
.q{display:grid;grid-template-columns:34px minmax(0,1fr) 170px;gap:18px;padding:16px 0;border-bottom:1px solid var(--rule);align-items:start}
|
||||
@media (max-width:760px){.q{grid-template-columns:28px minmax(0,1fr);gap:12px}.q .owner{grid-column:2}}
|
||||
.q .qn{font-family:var(--font-mono);font-size:11px;color:var(--brass);padding-top:3px}
|
||||
.q .qt{font-family:var(--font-display);font-size:14px;line-height:1.48;color:var(--ink-2)}
|
||||
.q .qt b{color:var(--ink);font-weight:700;display:block;margin-bottom:2px;font-size:14.5px}
|
||||
.owner{font-family:var(--font-mono);font-size:10px;letter-spacing:.05em;color:var(--ink-3);padding-top:4px}
|
||||
.owner .tag{display:inline-block;border:1px solid var(--rule-strong);padding:2px 7px;border-radius:2px}
|
||||
.owner .tag.need{border-color:var(--clay);color:var(--clay)}
|
||||
|
||||
/* ---------- misc ---------- */
|
||||
.numbers{font-family:var(--font-mono);font-size:12.5px;line-height:1.85;background:var(--surface);border-left:3px solid var(--l3);padding:16px 20px;margin:0 0 22px;overflow-x:auto;max-width:var(--measure)}
|
||||
.numbers .v{color:var(--ink);font-weight:600}
|
||||
.numbers .k{color:var(--ink-3)}
|
||||
pre{font-family:var(--font-mono);font-size:12.5px;line-height:1.68;background:var(--surface);border-left:3px solid var(--rule-strong);padding:16px 20px;overflow-x:auto;margin:0 0 22px;max-width:var(--measure);color:var(--ink-2)}
|
||||
.alt{border-bottom:1px solid var(--rule);padding:14px 0;max-width:var(--measure)}
|
||||
.alt:last-of-type{border-bottom:none}
|
||||
.alt b{font-family:var(--font-display);font-size:14px;display:block;margin-bottom:3px}
|
||||
.alt p{font-size:14.5px;margin:0;color:var(--ink-2)}
|
||||
.alt .verdict{font-family:var(--font-mono);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--clay)}
|
||||
footer{border-top:2px solid var(--ink);margin-top:20px;padding-top:22px;font-family:var(--font-mono);font-size:11px;letter-spacing:.06em;color:var(--ink-3);display:flex;justify-content:space-between;gap:20px;flex-wrap:wrap}
|
||||
.tm td,.tm th{text-align:center}
|
||||
.tm td:first-child,.tm th:first-child{text-align:left}
|
||||
.yes{color:var(--l4);font-weight:700}
|
||||
.no{color:var(--clay);font-weight:700}
|
||||
.kind{font-family:var(--font-mono);font-size:9px;letter-spacing:.09em;text-transform:uppercase;padding:2px 6px;border-radius:2px;white-space:nowrap;border:1px solid var(--rule-strong);color:var(--ink-3)}
|
||||
.kind.adv{border-color:var(--clay);color:var(--clay)}
|
||||
.routes{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:2px;background:var(--rule);border:1px solid var(--rule);margin:0 0 22px}
|
||||
.route{background:var(--surface);padding:16px 18px}
|
||||
.route h4{font-family:var(--font-display);font-weight:750;font-size:14px;margin:0 0 6px}
|
||||
.route p{font-family:var(--font-display);font-size:12.5px;line-height:1.45;color:var(--ink-2);margin:0;max-width:none}
|
||||
.route .tag{font-family:var(--font-mono);font-size:9px;letter-spacing:.1em;text-transform:uppercase;color:var(--brass);display:block;margin-bottom:8px}
|
||||
a:focus-visible,.rail a:focus-visible{outline:2px solid var(--brass);outline-offset:3px}
|
||||
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
|
||||
|
||||
</style>
|
||||
<div class="wrap"><header><div class="eyebrow"><span>CUST-ADR-004</span> <span class="stat">accepted · accepted-1</span> <span>the-custodian</span> <span>reviewed 2026-03-26</span><span>generated from canonical source — do not edit</span></div><h1>Connectivity-First Network Posture for Custodian Infrastructure</h1><p class="sub">Source: <code>the-custodian · canon/architecture/adr-004-connectivity-first-network-posture.md · 4039c9d1c08c92014ecc0a65dda63cc73ba187bb</code></p><p class="sub">Review due: 2026-09-26</p></header><div class="layout"><nav class="rail" aria-label="Sections"><ol><li><a href="#status"><span class="n">·</span>Status</a></li><li><a href="#context"><span class="n">·</span>Context</a></li><li><a href="#decision"><span class="n">·</span>Decision</a></li><li><a href="#rationale"><span class="n">·</span>Rationale</a></li><li><a href="#consequences"><span class="n">·</span>Consequences</a></li><li><a href="#alternatives-rejected"><span class="n">·</span>Alternatives Rejected</a></li></ol></nav><main><section id="status"><h2>Status</h2>
|
||||
<p>Accepted.</p>
|
||||
</section>
|
||||
<section id="context"><h2>Context</h2>
|
||||
<p>The Custodian infrastructure spans multiple machines: a primary workstation, a shared compute node (COULOMBCORE), and Railiance cluster nodes. Agents running on remote machines need to reach the state-hub API and MCP server, which live on the workstation. Human operators and agents also need to reach remote services (k3s API, Gitea, Temporal) from the workstation.</p>
|
||||
<p>Two network postures were considered for how these components communicate:</p>
|
||||
<p><strong>Option A — Connectivity-first:</strong> Components are connected by default via controlled, observable access paths (reverse SSH tunnels managed by ops-bridge). Isolation is added selectively where there is a specific threat model or compliance reason to do so.</p>
|
||||
<p><strong>Option B — Isolation-first (zero-trust):</strong> No component trusts any other by default. Every connection requires mutual authentication, short-lived credentials, and explicit authorisation at the point of use. Connectivity is earned, not assumed.</p>
|
||||
<p>This decision is architectural policy — it governs how ops-bridge tunnels are designed, how agent-to-hub communication works, and how new infrastructure components are onboarded.</p>
|
||||
</section>
|
||||
<section id="decision"><h2>Decision</h2>
|
||||
<p><strong>Connectivity-first, with isolation as a deliberate option.</strong></p>
|
||||
<p>The default posture for Custodian infrastructure is: components that need to work together are connected. Access paths are explicit, observable, and managed (via ops-bridge), but they are persistent by default rather than ephemeral. Isolation is introduced where there is a specific, articulated reason — not as a blanket policy applied uniformly.</p>
|
||||
</section>
|
||||
<section id="rationale"><h2>Rationale</h2>
|
||||
<h3>1. Scale and team size</h3>
|
||||
<p>The infrastructure is operated by a single human and a bounded set of automation agents. The overhead of zero-trust (credential rotation, SPIFFE/SPIRE identity, mTLS everywhere, short-lived tokens per request) is disproportionate for this scale. It would consume significant operational complexity without a commensurate security return.</p>
|
||||
<h3>2. Observability over perimeter hardening</h3>
|
||||
<p>The primary security control at this scale is <strong>observability</strong>: knowing what connected, when, from where, and what it did. ops-bridge provides this — every tunnel is named, actor-attributed, health-checked, and audited. A perimeter of invisible short-lived connections would actually reduce observability.</p>
|
||||
<h3>3. The threat model does not require zero-trust today</h3>
|
||||
<p>The main threats are:</p>
|
||||
<ul><li>A runaway agent consuming resources (mitigated by nproc/memory cgroups)</li><li>A compromised workload reaching state-hub and corrupting state (mitigated by the read-model design of state-hub — write surface is narrow and sanctioned)</li><li>An external attacker reaching internal services (mitigated by the tunnels being reverse SSH — no inbound ports exposed)</li></ul>
|
||||
<p>Zero-trust would address a different threat model: lateral movement between hostile tenants, or untrusted code running in the same environment as sensitive data. That is not the current situation.</p>
|
||||
<h3>4. Degrade-gracefully requires persistent connectivity</h3>
|
||||
<p>The Custodian's foundational value of <strong>local-first, degrade-gracefully</strong> requires that agents can orient themselves even when some connections are slow or partially degraded. Ephemeral connectivity (zero-trust tokens, service mesh sidecars) introduces additional failure modes that conflict with graceful degradation. Persistent SSH tunnels with auto-reconnect are more resilient to intermittent conditions.</p>
|
||||
<h3>5. Isolation remains the right choice in specific cases</h3>
|
||||
<p>Connectivity-first does not mean no isolation. The following cases call for explicit isolation and are handled separately:</p>
|
||||
<ul><li><strong>Tenant separation</strong> (when/if multi-user or multi-org) — each tenant gets its own network segment</li><li><strong>Privileged execution</strong> — CI runners and agent actions with write access to production systems run in ephemeral, isolated environments (per the Privileged Execution Control standard)</li><li><strong>Secrets</strong> — credentials are never transmitted over tunnels in plaintext; age-encrypted at rest, SOPS for config</li></ul>
|
||||
</section>
|
||||
<section id="consequences"><h2>Consequences</h2>
|
||||
<h3>Immediate</h3>
|
||||
<ul><li>ops-bridge tunnels are <strong>persistent</strong> (max_attempts: 0, auto-reconnect) and are treated as infrastructure, not one-off connections</li><li>Agents on remote machines check tunnel health at session start and restore dropped tunnels before accessing state-hub (documented in global CLAUDE.md)</li><li>New infrastructure components are onboarded with a named tunnel entry in <code>~/.config/bridge/tunnels.yaml</code> — not ad-hoc SSH commands</li></ul>
|
||||
<h3>Deferred</h3>
|
||||
<ul><li>If the infrastructure grows to multi-tenant or multi-operator, this decision should be revisited. At that point, isolation-first becomes proportionate.</li><li>If a security audit identifies a specific lateral movement risk, targeted isolation (network policy, mTLS for that service) is the response — not a wholesale posture change.</li></ul>
|
||||
</section>
|
||||
<section id="alternatives-rejected"><h2>Alternatives Rejected</h2>
|
||||
<h3>Zero-trust / isolation-first</h3>
|
||||
<p>Rejected for current scale. The operational overhead (credential lifecycle, service mesh, mutual TLS) is disproportionate, observability would decrease, and the threat model does not require it. Noted for re-evaluation at multi- tenant scale.</p>
|
||||
<h3>VPN (WireGuard / Tailscale)</h3>
|
||||
<p>Considered briefly. VPN would solve the connectivity problem but introduces a persistent network layer that all traffic traverses, reducing the explicitness of individual access paths. ops-bridge tunnels are per-service and per-actor, which gives better observability and blast-radius control. VPN is not ruled out as a future complement but is not the primary approach.</p>
|
||||
<h3>Ad-hoc SSH (no ops-bridge)</h3>
|
||||
<p>The pre-ops-bridge approach. Rejected because it has no health checks, no actor attribution, no audit log, and requires manual intervention to restore. ops-bridge formalises the same SSH tunnel pattern with operational discipline.</p>
|
||||
</section><footer><span>CUST-ADR-004 · accepted-1 · accepted</span><span>the-custodian · canon/architecture/adr-004-connectivity-first-network-posture.md · 4039c9d1c08c92014ecc0a65dda63cc73ba187bb</span></footer></main></div></div></html>
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
---
|
||||
id: CUST-ADR-004
|
||||
type: architecture-decision-record
|
||||
title: "Connectivity-First Network Posture for Custodian Infrastructure"
|
||||
status: accepted
|
||||
owner: the-custodian
|
||||
revision: "accepted-1"
|
||||
last_reviewed: "2026-03-26"
|
||||
review_interval: 6m
|
||||
decided_by: Bernd Worsch
|
||||
date: "2026-03-26"
|
||||
tags: ["architecture", "network", "ops-bridge", "connectivity", "isolation", "security-posture"]
|
||||
---
|
||||
|
||||
# ADR-004: Connectivity-First Network Posture for Custodian Infrastructure
|
||||
|
||||
## Status
|
||||
|
||||
Accepted.
|
||||
|
||||
## Context
|
||||
|
||||
The Custodian infrastructure spans multiple machines: a primary workstation, a
|
||||
shared compute node (COULOMBCORE), and Railiance cluster nodes. Agents running
|
||||
on remote machines need to reach the state-hub API and MCP server, which live
|
||||
on the workstation. Human operators and agents also need to reach remote
|
||||
services (k3s API, Gitea, Temporal) from the workstation.
|
||||
|
||||
Two network postures were considered for how these components communicate:
|
||||
|
||||
**Option A — Connectivity-first:** Components are connected by default via
|
||||
controlled, observable access paths (reverse SSH tunnels managed by ops-bridge).
|
||||
Isolation is added selectively where there is a specific threat model or
|
||||
compliance reason to do so.
|
||||
|
||||
**Option B — Isolation-first (zero-trust):** No component trusts any other by
|
||||
default. Every connection requires mutual authentication, short-lived
|
||||
credentials, and explicit authorisation at the point of use. Connectivity is
|
||||
earned, not assumed.
|
||||
|
||||
This decision is architectural policy — it governs how ops-bridge tunnels are
|
||||
designed, how agent-to-hub communication works, and how new infrastructure
|
||||
components are onboarded.
|
||||
|
||||
## Decision
|
||||
|
||||
**Connectivity-first, with isolation as a deliberate option.**
|
||||
|
||||
The default posture for Custodian infrastructure is: components that need to
|
||||
work together are connected. Access paths are explicit, observable, and managed
|
||||
(via ops-bridge), but they are persistent by default rather than ephemeral.
|
||||
Isolation is introduced where there is a specific, articulated reason — not as
|
||||
a blanket policy applied uniformly.
|
||||
|
||||
## Rationale
|
||||
|
||||
### 1. Scale and team size
|
||||
|
||||
The infrastructure is operated by a single human and a bounded set of
|
||||
automation agents. The overhead of zero-trust (credential rotation, SPIFFE/SPIRE
|
||||
identity, mTLS everywhere, short-lived tokens per request) is disproportionate
|
||||
for this scale. It would consume significant operational complexity without
|
||||
a commensurate security return.
|
||||
|
||||
### 2. Observability over perimeter hardening
|
||||
|
||||
The primary security control at this scale is **observability**: knowing what
|
||||
connected, when, from where, and what it did. ops-bridge provides this — every
|
||||
tunnel is named, actor-attributed, health-checked, and audited. A perimeter of
|
||||
invisible short-lived connections would actually reduce observability.
|
||||
|
||||
### 3. The threat model does not require zero-trust today
|
||||
|
||||
The main threats are:
|
||||
- A runaway agent consuming resources (mitigated by nproc/memory cgroups)
|
||||
- A compromised workload reaching state-hub and corrupting state (mitigated by
|
||||
the read-model design of state-hub — write surface is narrow and sanctioned)
|
||||
- An external attacker reaching internal services (mitigated by the tunnels
|
||||
being reverse SSH — no inbound ports exposed)
|
||||
|
||||
Zero-trust would address a different threat model: lateral movement between
|
||||
hostile tenants, or untrusted code running in the same environment as sensitive
|
||||
data. That is not the current situation.
|
||||
|
||||
### 4. Degrade-gracefully requires persistent connectivity
|
||||
|
||||
The Custodian's foundational value of **local-first, degrade-gracefully**
|
||||
requires that agents can orient themselves even when some connections are slow
|
||||
or partially degraded. Ephemeral connectivity (zero-trust tokens, service mesh
|
||||
sidecars) introduces additional failure modes that conflict with graceful
|
||||
degradation. Persistent SSH tunnels with auto-reconnect are more resilient to
|
||||
intermittent conditions.
|
||||
|
||||
### 5. Isolation remains the right choice in specific cases
|
||||
|
||||
Connectivity-first does not mean no isolation. The following cases call for
|
||||
explicit isolation and are handled separately:
|
||||
|
||||
- **Tenant separation** (when/if multi-user or multi-org) — each tenant gets
|
||||
its own network segment
|
||||
- **Privileged execution** — CI runners and agent actions with write access to
|
||||
production systems run in ephemeral, isolated environments (per the
|
||||
Privileged Execution Control standard)
|
||||
- **Secrets** — credentials are never transmitted over tunnels in plaintext;
|
||||
age-encrypted at rest, SOPS for config
|
||||
|
||||
## Consequences
|
||||
|
||||
### Immediate
|
||||
|
||||
- ops-bridge tunnels are **persistent** (max_attempts: 0, auto-reconnect) and
|
||||
are treated as infrastructure, not one-off connections
|
||||
- Agents on remote machines check tunnel health at session start and restore
|
||||
dropped tunnels before accessing state-hub (documented in global CLAUDE.md)
|
||||
- New infrastructure components are onboarded with a named tunnel entry in
|
||||
`~/.config/bridge/tunnels.yaml` — not ad-hoc SSH commands
|
||||
|
||||
### Deferred
|
||||
|
||||
- If the infrastructure grows to multi-tenant or multi-operator, this decision
|
||||
should be revisited. At that point, isolation-first becomes proportionate.
|
||||
- If a security audit identifies a specific lateral movement risk, targeted
|
||||
isolation (network policy, mTLS for that service) is the response — not a
|
||||
wholesale posture change.
|
||||
|
||||
## Alternatives Rejected
|
||||
|
||||
### Zero-trust / isolation-first
|
||||
|
||||
Rejected for current scale. The operational overhead (credential lifecycle,
|
||||
service mesh, mutual TLS) is disproportionate, observability would decrease,
|
||||
and the threat model does not require it. Noted for re-evaluation at multi-
|
||||
tenant scale.
|
||||
|
||||
### VPN (WireGuard / Tailscale)
|
||||
|
||||
Considered briefly. VPN would solve the connectivity problem but introduces
|
||||
a persistent network layer that all traffic traverses, reducing the
|
||||
explicitness of individual access paths. ops-bridge tunnels are per-service
|
||||
and per-actor, which gives better observability and blast-radius control.
|
||||
VPN is not ruled out as a future complement but is not the primary approach.
|
||||
|
||||
### Ad-hoc SSH (no ops-bridge)
|
||||
|
||||
The pre-ops-bridge approach. Rejected because it has no health checks, no
|
||||
actor attribution, no audit log, and requires manual intervention to restore.
|
||||
ops-bridge formalises the same SSH tunnel pattern with operational discipline.
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"scope": "Fresh local preview only; no sourcecommit or live publication claimed",
|
||||
"proof": [
|
||||
{
|
||||
"id": "CUST-ADR-002",
|
||||
"immutable_accepted1_preserved_byte_exact": true,
|
||||
"served_page_sha256": "ea4eae05b62d50cd8204a47c1a4f3e3e0beda571880ed1de93ccbb17987d84aa",
|
||||
"current_accepted2_has_dated_review": true,
|
||||
"original_decided_by": "Bernd Worsch",
|
||||
"original_decision_date": "2026-03-12",
|
||||
"current_currency": "current"
|
||||
},
|
||||
{
|
||||
"id": "CUST-ADR-004",
|
||||
"immutable_accepted1_preserved_byte_exact": true,
|
||||
"served_page_sha256": "3db1b0f5c50543ff0fb022ea67f1d85b30f8ff691eaa1a2443220b7474b54de7",
|
||||
"current_accepted2_has_dated_review": true,
|
||||
"original_decided_by": "Bernd Worsch",
|
||||
"original_decision_date": "2026-03-26",
|
||||
"current_currency": "current"
|
||||
}
|
||||
],
|
||||
"records": [
|
||||
{
|
||||
"id": "CUST-ADR-002",
|
||||
"title": "Custodian Agent Runtime \u2014 v0.1 Bootstrap Design",
|
||||
"status": "accepted",
|
||||
"revision": "accepted-2",
|
||||
"owner": "the-custodian",
|
||||
"last_reviewed": "2026-10-11",
|
||||
"review_due": "2027-04-11",
|
||||
"currency": "current",
|
||||
"lifecycle": "active",
|
||||
"canonical_path": "adr/custodian-agent-runtime/v1/index.html",
|
||||
"revision_path": "adr/custodian-agent-runtime/v1/revisions/accepted-2/index.html",
|
||||
"source_repo": "the-custodian",
|
||||
"source_path": "canon/architecture/adr-002-custodian-agent-runtime-design.md",
|
||||
"source_revision": "sha256:b860b02b1e8f47b914d41c6ecf67f451e86326ed51c7c53dcc6b7870fc889e42",
|
||||
"source_digest": "b860b02b1e8f47b914d41c6ecf67f451e86326ed51c7c53dcc6b7870fc889e42"
|
||||
},
|
||||
{
|
||||
"id": "CUST-ADR-004",
|
||||
"title": "Connectivity-First Network Posture for Custodian Infrastructure",
|
||||
"status": "accepted",
|
||||
"revision": "accepted-2",
|
||||
"owner": "the-custodian",
|
||||
"last_reviewed": "2026-10-11",
|
||||
"review_due": "2027-04-11",
|
||||
"currency": "current",
|
||||
"lifecycle": "active",
|
||||
"canonical_path": "adr/custodian-connectivity-first/v1/index.html",
|
||||
"revision_path": "adr/custodian-connectivity-first/v1/revisions/accepted-2/index.html",
|
||||
"source_repo": "the-custodian",
|
||||
"source_path": "canon/architecture/adr-004-connectivity-first-network-posture.md",
|
||||
"source_revision": "sha256:4f9978b40c104600c0ccb981a8ee52376a5d3003da4786752029af524b7dede7",
|
||||
"source_digest": "4f9978b40c104600c0ccb981a8ee52376a5d3003da4786752029af524b7dede7"
|
||||
}
|
||||
]
|
||||
}
|
||||
130
docs/evidence/2026-10-11-adr-currency-review/review.json
Normal file
130
docs/evidence/2026-10-11-adr-currency-review/review.json
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
{
|
||||
"review_date": "2026-10-11",
|
||||
"reviewer": "Dedicated Custodian technical author review under current consolidation assignment",
|
||||
"scope": "Source/currency review of two accepted historical ADRs; no live audit, ratification, promotion or privileged authorization",
|
||||
"originals": [
|
||||
{
|
||||
"id": "CUST-ADR-002",
|
||||
"path": "canon/architecture/adr-002-custodian-agent-runtime-design.md",
|
||||
"original_revision": "accepted-1",
|
||||
"original_sha256": "6aef66cd5cf71a48f5b4e14401dc19a755e2b182445b272d5952df0eb0dea8ec",
|
||||
"historical_snapshot": "docs/evidence/2026-10-11-adr-currency-review/adr-002-accepted-1.md",
|
||||
"annotated_revision": "accepted-2",
|
||||
"normative_decision_changed": false
|
||||
},
|
||||
{
|
||||
"id": "CUST-ADR-004",
|
||||
"path": "canon/architecture/adr-004-connectivity-first-network-posture.md",
|
||||
"original_revision": "accepted-1",
|
||||
"original_sha256": "3b68adfa6ab329e73f857cf691dc405136d2d66a0135e2c37c236aabe4659557",
|
||||
"historical_snapshot": "docs/evidence/2026-10-11-adr-currency-review/adr-004-accepted-1.md",
|
||||
"annotated_revision": "accepted-2",
|
||||
"normative_decision_changed": false
|
||||
}
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/runtime/agent.py",
|
||||
"sha256": "3b1e6dfd8bfca20d91cd17b75ac03739a97e3ca2dd35719c2b14f76d5eab653e"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/runtime/actions.py",
|
||||
"sha256": "b69f6bc4e8ec463526a578ed28627884df6633bf13d56bc110fb77815d2e0f53"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/runtime/context.py",
|
||||
"sha256": "988e37757360207356b8292599c812ca56450ba53ea6c25fa8486a4b02d17f08"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/runtime/README.md",
|
||||
"sha256": "c9bcfe727705b1503447d863f6fed4a84e97eb18a84b335c9073dac6dbb756e4"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/agent-autonomy-decision.md",
|
||||
"sha256": "4f65dfd2b56b67cd64eb9577644d574d2e687adcfd8b5bdb65a2e0d14f8bf1ff"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/agent-environment-orientation.md",
|
||||
"sha256": "6eb712593adaeb2efd8c69edb22b02662e6c9b155f84f1559e7bdaeb76730439"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/docs/kubernetes-change-gate-decision.md",
|
||||
"sha256": "063d9625f9de2d40d1f4e95f9423a54186fa0b429d76d5f173caa646317a50ab"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.kaizen/agents/custodian-codex/supervision.json",
|
||||
"sha256": "28b798e04a310eb0c502bb80780f6f0586f61326f1e16e1e85d13863384032b5"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/canon/architecture/adr-001-workplans-as-repo-artefacts.md",
|
||||
"sha256": "183023ee57bae9c29e726fec5ee0361633fe3a2b182436a57869ae4b2a27af24"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/canon/architecture/adr-012-projection-source-and-preliminary-overlay.md",
|
||||
"sha256": "b5e8582459f546ae789ad5fd62f458454aa19997b520e32b6b9f792d6af55987"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/canon/standards/autonomy-lanes_v0.1.md",
|
||||
"sha256": "106cb0c99f73983199c9f1d8491143aa2a2eb41ee450a02ec7968d3763f30399"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/ops-bridge/INTENT.md",
|
||||
"sha256": "2134f693649803414d7ab2f9839416f1fe06169aa86732e64c9dd962c35f412b"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/ops-bridge/SCOPE.md",
|
||||
"sha256": "63cd52d1cdf38e72af0285c25aa2292f959080c6befe565a1d2690747a813975"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/net-kingdom/canon/standards/security-layer-model_v0.8.md",
|
||||
"sha256": "3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b"
|
||||
}
|
||||
],
|
||||
"test": {
|
||||
"cwd": "/home/worsch/the-custodian/runtime",
|
||||
"argv": [
|
||||
"python3",
|
||||
"-m",
|
||||
"pytest",
|
||||
"-q",
|
||||
"-p",
|
||||
"no:cacheprovider",
|
||||
"tests"
|
||||
],
|
||||
"env_overrides": {
|
||||
"PYTHONPATH": ".",
|
||||
"PYTHONDONTWRITEBYTECODE": "1"
|
||||
},
|
||||
"exit_code": 0,
|
||||
"stdout": "................................ [100%]\n32 passed in 0.15s\n",
|
||||
"stderr": ""
|
||||
},
|
||||
"findings": [
|
||||
"ADR002 remains accurate as bootstrap architecture; current deployment claims unverified and not reaffirmed.",
|
||||
"ADR002 D4 does not supply Codex autopilot, scope authorization or file-source override.",
|
||||
"ADR004 connectivity-only intent remains compatible; historical Hub topology and broad security-assurance rationale are obsolete or unproven and prominently qualified.",
|
||||
"Existing September supervision/change gates and accepted NetKingdom model supply mandatory current controls; no fresh authority or posture change requested.",
|
||||
"accepted-1 bytes retained exactly; publisher must retain existing immutable accepted-1 route and publish reviewed annotations as accepted-2, not overwrite history."
|
||||
],
|
||||
"new_human_decision_required": false,
|
||||
"reason": "No original architectural decision or current authority is expanded; annotations apply existing accepted constraints and correct historical/current scope.",
|
||||
"historical_publication": {
|
||||
"strategy": "Explicit per-document historical_revisions preserves hash-pinned actual served accepted-1 HTML from source-owned historical artifacts, even in a clean fresh build. Source snapshot identity/revision/digest and page provenance commit/digest are checked; conflicting immutable existing bytes fail.",
|
||||
"served_original_source_commit": "4039c9d1c08c92014ecc0a65dda63cc73ba187bb",
|
||||
"actual_served_pages": [
|
||||
{
|
||||
"id": "CUST-ADR-002",
|
||||
"url": "https://policy.coulomb.social/adr/custodian-agent-runtime/v1/revisions/accepted-1/index.html",
|
||||
"sha256": "ea4eae05b62d50cd8204a47c1a4f3e3e0beda571880ed1de93ccbb17987d84aa"
|
||||
},
|
||||
{
|
||||
"id": "CUST-ADR-004",
|
||||
"url": "https://policy.coulomb.social/adr/custodian-connectivity-first/v1/revisions/accepted-1/index.html",
|
||||
"sha256": "3db1b0f5c50543ff0fb022ea67f1d85b30f8ff691eaa1a2443220b7474b54de7"
|
||||
}
|
||||
],
|
||||
"publisher_tests": "30 tests passed, including five new retention/corruption/escape regression tests.",
|
||||
"actual_preview": "publisher-preview-proof.json proves both old immutable URLs byte-exact and current accepted-2 annotation pages in a fresh local output. No live publication claimed.",
|
||||
"inventory": "Only existing two canonical corpus records remain active publications. Historical source/page artifacts live under docs/evidence outside canon/ADR selectors; they are explicit build dependencies in publication.json, not extra governing records or stale documents hidden from currency checks."
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue