diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml new file mode 100644 index 0000000..1557a99 --- /dev/null +++ b/.github/workflows/conformance.yml @@ -0,0 +1,33 @@ +name: conformance + +# Scoped to the conformance scaffold so the doc-only surface of the repo is untouched. +on: + push: + branches: ["**"] + paths: + - "conformance/**" + - ".github/workflows/conformance.yml" + pull_request: + paths: + - "conformance/**" + - ".github/workflows/conformance.yml" + +jobs: + weightlessness-gate: + name: weightlessness gate (ADR-020) + runs-on: ubuntu-latest + defaults: + run: + working-directory: conformance + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "20" + cache: npm + cache-dependency-path: conformance/package-lock.json + - run: npm ci + - name: typecheck + run: npm run typecheck + - name: protocol unit tests + conformance suite (reference adapter) + run: npm run test:example diff --git a/.gitignore b/.gitignore index d5c0b17..5d608f3 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,8 @@ Thumbs.db *.bak-* *.bak.* _archive_backup/ + +# Conformance scaffold build artifacts +conformance/node_modules/ +conformance/coverage/ +conformance/dist/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 8460522..f544508 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,114 @@ Reverse-chronological record of architectural decisions and significant artifact --- +## [2026-06-02] **design/ element specs + ROADMAP-MONTHLY.md** + +Two drill-downs on top of the long-range roadmap. + +**`design/` — element design specifications.** A new directory drilling each architectural element to the engineering level across four facets: **Technology** (substrate per ADR-014 enforcement layer 1→4), **Application** (consumers/use cases), **Algorithm** (procedures, invariants, complexity, security properties), and **Pseudocode** (implementable, kept consistent with the running primitives in `conformance/src/protocol/`). `design/INDEX.md` catalogs all 20 elements (grouped: core trust / decision / provenance / wire / tri-surface / enforcement substrate / services) with status flags and the drill order. First batch — the trust spine with running code: +- `01-capability.md` — unforgeable references; attenuate-only sets; invariants I1 (unforgeability) / I2 (no amplification) / I3 (monotone cascade); per-layer representation (object ref → fd/Capsicum → signed TPM token). +- `02-lease.md` — boundary-amortized authority; the **revocation lifecycle** resolving fast-validation vs prompt-revocation via short-TTL + epoch bump + targeted revocation list (bounded, *disclosed* staleness — the WEIGHTLESS/ADR-015 trade made explicit). +- `03-mediation.md` — the interception point per layer; invariants M1 (no false denial on routine path) / M2 (UNNAMEABLE, never DENIED) / M3 (byte-identity); the Tier-C boundary evaluator. Tied directly to ADR-020 Groups W/H/R. + +**`ROADMAP-MONTHLY.md` — month-by-month execution.** Expands the quarter-level roadmap to 60 monthly slots (M01 = 2026-07 → M60 = 2031-06). Year 1 planned at task level across all five tracks; Years 2–5 at monthly-milestone level (granularity decreases with distance, by design — far-out monthly task lists are fiction; each annual exit gate re-plans the next year to task level). Gate markers (🟢 entry/exit · 🔺 layer go/no-go · ★ keystone) throughout; the two outranking numbers flagged — the second independent conformant implementation (M16→M24) and self-sustaining-with-neutrality (M54). + +--- + +## [2026-06-02] **ROADMAP.md — long-range program plan (inception → Year 5)** + +New top-level companion artifact: the granular operating plan from inception to a five-year de-facto-standard horizon. Operationalizes the existing architecture (ADR-014 layer trajectory, ADR-009 profiles, ADR-015 methodology, ADR-020 conformance gate, the tri-surface design) and adds the standardization, funding, governance, and org tracks the ADRs presuppose but don't specify. + +**Posture (decided 2026-06):** open-core (ASHE Apache-2.0 + Continuum commercial reference impl); grant/standards-body funded; north star = **de facto cross-vendor standard** (adoption wins over revenue when they trade off). + +**Structure:** three tracks (Standard / Implementation / Adoption) + two enabling tracks (Sustainability / Org & Governance); version↔layer↔year mapping (v0 Layer-1 → v6 Layer-4 pilot); quarter-level deliverables per year with explicit entry/exit **gates**; a grant/sponsorship funding plan; a lean standards/eng-weighted org plan; per-year KPI targets (Floor/Target/Stretch graded); an 8-item risk register; and the sequencing logic (spec freeze → conformance suite → independent implementations → standards motion → layers 1→4, never skipped). + +**Keystone metric:** the *second independent conformant implementation* (Y2) — the line between a document and a real standard. Linked from README read-order as item 5. + +--- + +## [2026-05-31] **conformance/ — CI workflow + nascent reference protocol primitives** + +Two follow-ons to the executable conformance suite. + +**GitHub Actions** ([`.github/workflows/conformance.yml`](.github/workflows/conformance.yml)) — the repo's first CI, scoped via `paths:` to `conformance/**` so the doc-only surface is untouched. Runs `npm ci → typecheck → test:example` on Node 20: the weightlessness gate (ADR-020) now has a green check on every PR that touches it. + +**`conformance/src/protocol/`** — the example adapter is no longer a toy; it delegates to a real, in-memory **reference implementation** of ASHE's object-capability core, the embryonic Continuum-style impl the suite wraps: + +- **`capability.ts`** — *unforgeable* capabilities (the mint token is module-private; `new Capability(...)` from outside throws) and `CapabilitySet` whose only derivation is `attenuate()`. There is no `grant`/`union`: authority amplification is **unconstructable**, not merely checked — the object-capability primitive (VISION §1, ADR-003) made structural. +- **`actor.ts`** — principals holding a set and nothing ambient; `spawn()` attenuates, so a sub-actor exceeding its parent cannot be built (cascade attenuation, ADR-017). +- **`lease.ts`** — boundary-amortized standing authority with a TTL (WEIGHTLESS amortization; ADR-017 standing capabilities). +- **`tier.ts`** — routine (A/B) vs the deliberate-weight Tier-C boundary. +- **`mediation.ts`** — the interception point (ADR-007) applied structurally: routine held actions pass through with no boundary step and byte-identical payload; an unheld capability is `UNNAMEABLE`, never `DENIED`. +- **`audit.ts`** — tamper-evident **audit-by-construction** (ADR-013 Audit service; ADR-016 provenance): an append-only, SHA-256 hash-chained log. `verify()` catches any reorder/edit/drop of a sealed record; the Mediator optionally emits one record per decision (including `UNNAMEABLE` attempts), as a *local* append off the critical path — not a round-trip. +- **`intent.ts`** — declare-once **intent reconciliation** (VISION §6; ADR-017 C2): an in-scope, unexpired action reconciles silently (the frictionless routine path keeps accountability without re-approval); out-of-scope or expired escalates. + +21 protocol unit tests + 11 conformance assertions; `npm run test:example` → **32/32 green**, `tsc --noEmit` clean. `npm test` (no adapter) runs the units and skips the conformance groups. The protocol module is the seed of the reference implementation the ADRs describe; subsequent work grows it (the validation graph, wire-format projections, the lease/revocation lifecycle). + +--- + +## [2026-05-31] **conformance/ — executable weightlessness-gate suite (ADR-020)** + +First runnable arm of the spec. Turns ADR-020's four conformance groups into an executable scaffold under [`conformance/`](conformance/) — the spec repo's first code. + +- **Adapter contract** (`src/adapter.ts`): the seam an implementation-under-test implements; four method blocks map one-to-one to the four facets (what/how/when/where). The suite touches no implementation directly. +- **Language-neutral manifest** (`src/manifest.ts`): the 11 tests (W1–W3, H1–H3, N1–N2, R1–R3) as typed data, keyed by id so non-TypeScript implementations report comparable results across the four ADR-014 layers. +- **Four test files** (`tests/group-{w,h,n,r}-*.test.ts`): real assertions, not empty stubs. Sharpest checks — W1 fails a SUT that returns **DENIED** for an unauthorized action (it was nameable, hence evaluated); N2 fails a SUT where disabling ASHE leaves a guarded action **reachable** (a removable front gate, not the system's shape); R2 fails a SUT that gates the routine 98% like the Tier C 2%. +- **Graded, not binary**: Group H records `literal-zero` (structural, Layer 3/4) vs `amortized-small` (procedural, Layer 1, disclosed) per ADR-015 — a Layer-1 implementation passes honestly rather than by over-claiming. +- **Self-verifying**: an illustrative correctly-applied adapter (`src/examples/structural-reference-adapter.ts`) makes `npm run test:example` green (11/11); with no adapter configured the suite skips all groups (it makes no claim about an unwired SUT). + +**Stack**: TypeScript + vitest (the ADR-011/017 P0 reference stack). `node_modules` git-ignored; lockfile committed. **Status**: v0.1 scaffold; the weightlessness gate is the first of the conformance commitments in ADR-001/015/017. + +--- + +## [2026-05-30] **ADR-020 — weightlessness is conformant only under proper application** + +Promotes the "proper application — the resolving discipline" principle from a WEIGHTLESS.md design note to a **binding conformance gate**. "Weightless" becomes a falsifiable four-part predicate, not an adjective. + +**The gate**: an ASHE implementation MAY claim the four hard invariants (no delay / no bandwidth / no data alteration / no interference) on the steady-state path **only if** it satisfies all four facets of proper application, conjunctively: + +- **What** — authority modeled as object-capability (*held or absent*); an unauthorized action MUST be *unnameable*, not checked-and-denied. +- **How** — the routine-path boundary MUST be **structural** (the substrate's own mechanism per [ADR-014](decisions/ADR-014-phased-enforcement-model.md) Layer 2/3/4), not a procedural check ASHE runs as an added step. +- **When** — the boundary MUST be established **at construction** (wall-up-first, `ashe workspace init` as step 1 per [ADR-017](decisions/ADR-017-sealed-workspace-foundational-dev-pattern.md)), not bolted on as a front gate. +- **Where** — literal-zero applied to the ~98% routine path; deliberate weight concentrated at the ~2% Tier C boundary. Uniform enforcement fails this facet. + +**What it forbids**: claiming "weightless" for a fast-but-real per-action check (a fast check is still a check), a permission-flag/identity model, a retrofitted gate, or uniform enforcement of the routine path. + +**Layer-1 caveat**: cooperating-SDK implementations are not disqualified — they MUST disclose, per [ADR-015](decisions/ADR-015-validation-methodology-and-tiered-claims.md) evidence grades, that no-delay/no-bandwidth are **amortized-small (Layer 1)** rather than **literal-zero (Layer 3/4 structural property)**. The gate distinguishes *honest amortized-small* from *false literal-zero*. + +**Conformance suite**: four test groups (W/H/N/R, one per facet), conjunctive — all four MUST pass. Group W tests unnameability + zero-ambient-authority; Group H tests no-added-step + byte-identity + layer-disclosure; Group N tests construction-order + no-front-gate (disabling ASHE must make guarded actions *unreachable*, not *ungated-but-reachable*); Group R tests path-classification + no-uniform-enforcement + friction-frequency. Results are graded per invariant (literal-zero vs amortized-small-disclosed), never binary-by-assertion. + +**Status**: Accepted. `Builds on` ADR-007, ADR-009, ADR-014, ADR-015, ADR-017. No ADR superseded. INDEX.md and WEIGHTLESS.md cross-reference it. + +--- + +## [2026-05-30] **WEIGHTLESS.md — the weightlessness design principle** + +New companion artifact consolidating ASHE's "frictionless by mandate / TLS for the agent layer" promise into a single named engineering property and budget. + +**Core principle**: weightlessness is the *placement* of cost, not its absence — *pay once at an amortizable boundary (handshake, intent declaration, lease issuance); make the steady-state per-action path a local check with no network callout, no model token, and no human prompt.* The same handshake-vs-symmetric-crypto split TLS uses, generalized from bytes to actions. + +**What it adds**: + +1. **Axes-of-weight table** — names every axis a protocol layer can be *felt* on (latency, wire/token, cognitive, footprint, model-layer, adoption) and maps each to the existing ASHE mechanism that drives it toward zero, with ADR anchors. + +2. **Hot-path budget** — promotes the [ADR-007](decisions/ADR-007-interception-chain-pattern.md) locally-validated capability token from "v1 optimization" to the steady-state default; separates the rare slow path (Tier C, broker callout) from the routine fast path (in-process validation, sub-millisecond). + +3. **Evidence grades** — weightlessness claims tiered Floor/Target/Stretch per [ADR-015](decisions/ADR-015-validation-methodology-and-tiered-claims.md); only the <5 ms p99 interceptor bound and the arXiv 2511.23281 structured-over-HTML result are Floor-grade. + +4. **Honest limits** — the Tier C friction floor, cold-start boundary cost, the revocation-vs-token-speed trade, and audit-write cost are named as irreducible; weightlessness is *over a session*, not *over a single isolated call*. + +5. **Conformance budget** — a checkable per-property budget table; the one-line test: a routine action costs no round-trip, no token, no human attention — and the audit record still exists. + +6. **The four hard invariants** — strict statement of weightlessness as absolutes: *no delay, no added bandwidth, no data alteration, no interference.* No-data-alteration is Floor-contractual ([ADR-007](decisions/ADR-007-interception-chain-pattern.md): idempotent, no dispatch-state mutation — pass/deny, never rewrite); no-interference-with-surfaces is Floor-constructional ([ADR-018](decisions/ADR-018-well-known-ashe-web-side-interaction-point.md) additive + [ADR-009](decisions/ADR-009-deployment-profiles.md) graceful degradation). The honest tension is named: *active enforcement is interference by definition* — literal-zero holds only for pre-authorized actions and structurally-bounded denials, with remaining enforcement weight concentrated at the rare Tier C boundary where interference is the intended function. + +7. **Structural vs procedural mediation** — the path to literal zero on delay/bandwidth is making the capability boundary *structural* (object-capability: an unforgeable reference you were never handed is an absence, not a denied check) rather than *procedural* (a check that runs). True-zero is a Layer 3/4 property per [ADR-014](decisions/ADR-014-phased-enforcement-model.md) — where the boundary stops being a step on the path and becomes part of the system's shape, enforced by the substrate's existing mechanism (type system / OS capability / hardware root) at no added cost. + +8. **Proper application — the resolving discipline** — weightlessness is not a mechanism you install; it is the default state of a system whose authority boundaries were applied correctly, forfeited the instant they are applied incorrectly. "Properly apply" is four facets of one discipline, all required together: the object-capability primitive (*what*), structurally (*how*), at construction (*when*), on the dominant path (*where*). Drop any one and weight reappears. The point: proper application *removes* the work rather than accelerating it — a fast check is still a check, still weight; weightless means there is no check at all on the path that matters. + +**Status**: Companion design note (v0.x), consistent with the existing artifact bundle. No ADR superseded; cross-references [ADR-007](decisions/ADR-007-interception-chain-pattern.md), [ADR-009](decisions/ADR-009-deployment-profiles.md), [ADR-012](decisions/ADR-012-wire-format-grpc-protobuf-with-projections.md), [ADR-017](decisions/ADR-017-sealed-workspace-foundational-dev-pattern.md), [ADR-018](decisions/ADR-018-well-known-ashe-web-side-interaction-point.md). + +--- + ## [2026-05-28] **ADR-019 v0.2 — phor-scoped governance refinement** ADR-019 amended to v0.2 incorporating phor-scoped governance frame as a load-bearing architectural layer that mediates occupant authority. diff --git a/README.md b/README.md index f54cbdf..e11f827 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,8 @@ The architectural thesis: **bounded outcomes ≠ censored behavior**. The model | 1 | [MANIFESTO.md](./MANIFESTO.md) | The opener — what ASHE is in one paragraph; the bounded-outcomes commitment; what ASHE is not | | 2 | [CASE-FOR-NOW.md](./CASE-FOR-NOW.md) | The urgency case anchored on Anthropic's Glasswing operational data + the CVD disclosure-funnel quantification | | 3 | [VISION.md](./VISION.md) | The technical-strategic vision; start with §0 (keystone metaphor) + §1 (capability-broker thesis) | -| 4 | [decisions/INDEX.md](./decisions/INDEX.md) | Architectural decision records (ADRs 001-019) — the design discipline trail | +| 4 | [decisions/INDEX.md](./decisions/INDEX.md) | Architectural decision records (ADRs 001-020) — the design discipline trail | +| 5 | [ROADMAP.md](./ROADMAP.md) | The long-range program plan — inception → Year-5 de-facto standard; tracks, gates, funding, metrics, risks | ## Provenance diff --git a/ROADMAP-MONTHLY.md b/ROADMAP-MONTHLY.md new file mode 100644 index 0000000..7b5e165 --- /dev/null +++ b/ROADMAP-MONTHLY.md @@ -0,0 +1,174 @@ +# ASHE — Month-by-Month Execution Plan + +Companion to [ROADMAP.md](ROADMAP.md). The roadmap sets phases, tracks, gates, and metrics; this document schedules them **month by month**. It inherits the roadmap's posture (open-core; grant/standards-funded; de-facto-standard north star) and its tracks (**A** Standard · **B** Implementation · **C** Adoption · **D** Sustainability · **E** Org/Governance). + +**Anchor:** M01 = 2026-07. Months run M01…M60 (five years). **Granularity decreases with distance, honestly:** Year 1 (M01–M12) is planned at task level; Years 2–5 (M13–M60) at monthly-milestone level — far-out monthly task lists are fiction, and naming them as milestones is the disciplined alternative. Each annual exit gate re-plans the following year to task level. + +**Gate legend:** 🟢 entry/exit gate · 🔺 discrete go/no-go (layer spike) · ★ keystone milestone. + +--- + +## Year 1 — *Freeze v1; plant the standard* (M01–M12, 2026-07 → 2027-06) + +> Year-1 theme: a frozen, implementable spec v1.0; a Layer-1 reference implementation a second party can independently re-implement; the standardization motion formally begun. **Entry 🟢:** inception exit gate passed (E2E lease→mediate→audit works; spec skeleton; ≥1 grant submitted; governance vehicle chosen). + +### M01 — 2026-07 · *Spec extraction begins* +- **A:** Stand up `SPEC.md` skeleton; extract the normative surface from VISION/ADRs into numbered sections (capability grammar, lease lifecycle, intent, audit record, wire contract). Land Element specs 01–03 (done) into the normative spec by reference. +- **B:** Consolidate `conformance/src/protocol/` into a published `ashe-core` package boundary (public API: `Issuer`, `CapabilitySet`, `LeaseAuthority`, `Mediator`, `AuditLog`, `IntentContext`). +- **C:** Finalize the design-partner shortlist (3–5); send the one-page integration pitch. +- **D:** Submit grant applications #1–#2 (NLnet/NGI Zero; an AI-safety funder). Open the governance-vehicle decision memo. +- **E:** Confirm core team (protocol editor + 1 engineer). Set the public RFC process (how spec changes are proposed/disposed). + +### M02 — 2026-08 · *Revocation lifecycle (the named gap)* +- **B:** Implement the lease **revocation lifecycle** (Element 02 §3.3): short-TTL + epoch bump + targeted revocation list; fast-path validation stays callout-free. Add revocation unit tests. +- **A:** `SPEC.md` §lease complete, including the bounded-staleness disclosure (revocation-vs-speed trade, ADR-015). +- **C:** First design-partner technical call; capture their integration constraints. +- **D:** Governance-vehicle decision **made** (default: host under an existing foundation). Draft the governance charter + IP policy. + +### M03 — 2026-09 · *Identity + wire contract drafted* 🟢 (Q1 review) +- **B:** Element 04 (Identity/Principal) — OIDC token → principal → `CapabilitySet` binding (ADR-002); actor authentication at lease issuance. +- **A:** `SPEC.md` §identity + §wire (Protobuf canonical message shapes for capability/lease/audit; JSON/TOON projections per ADR-012/006). +- **C:** Second design partner signed (LOI/informal). +- **E:** Publish governance charter draft for comment. +- **Gate 🟢:** Q1 review — spec skeleton complete through identity+wire; revocation shipped; ≥1 grant decision pending or in. + +### M04 — 2026-10 · *Validation graph scaffold* +- **B:** Element 05 — standalone validation-graph engine scaffold (ADR-008/010): node interface, short-circuit eval, default deterministic + tiny-ONNX evaluator stub. Wire it as the Tier-C `evaluate_boundary` (Element 03 §3.2). +- **A:** `SPEC.md` §validation-graph (informative: the engine; normative: the decision contract). +- **C:** Design partner #1 starts an integration spike against `ashe-core`. + +### M05 — 2026-11 · *Intent reconciliation + tier scoring* +- **B:** Harden Element 07 (intent reconciliation) into the boundary evaluator; Element 06 — move tier classification from static registry toward a scored model (anomaly score input). +- **A:** `SPEC.md` §intent + §tier complete. **`SPEC.md` v0.9 feature-complete.** +- **C:** Partner spike #1 produces feedback → spec errata queue. + +### M06 — 2026-12 · *Public RFC opens* ★ +- **A:** ★ **Open the 8-week public RFC period on `SPEC.md` v0.9** (GitHub-based). Publish the conformance-suite v1 spec (weightlessness gate + a functional gate: lease/scope/audit/intent correctness). +- **B:** `ashe-core` passes the weightlessness gate *and* the new functional gate in CI. +- **D:** Grant #1 decision expected; if no, submit #3–#4. Half-year funding review. +- **E:** Mid-year org review; bring on fractional devrel. + +### M07 — 2027-01 · *RFC intake* +- **A:** RFC comment intake + public disposition log (accept/reject/defer with rationale — the ADR discipline applied to spec comments). +- **B:** `ashe workspace init` MVP begins (Element 12 / ADR-017): wall-up-first; DevContainer adapter as first isolation substrate. +- **C:** Recruit a *second-implementation* steward — the Y2 keystone needs a running start (could be a community port or a partner). + +### M08 — 2027-02 · *RFC closes; freeze prep* +- **A:** RFC period closes; integrate accepted changes; produce the v1.0 release candidate. Cut the change-control process (semver, no silent breaking changes). +- **B:** `ashe workspace init` MVP usable end-to-end (init → sealed perimeter → standing capabilities → audited actions). +- **C:** Quickstart "hello, capability lease" (≤15 min) drafted. + +### M09 — 2027-03 · *Spec v1.0 FREEZE* ★🟢 (Q3 review) +- **A:** ★ **Freeze `SPEC.md` v1.0** (Floor). v1.0 commits the protocol *shape* that admits Layers 2–4 without rewrite (ADR-014). Tag the conformance suite to v1.0. +- **B:** `ashe-core` v1.0 tagged; conformant against the frozen spec. +- **Gate 🟢:** Q3 review — spec frozen, ref impl conformant, change control live. + +### M10 — 2027-04 · *First external E2E* ★ +- **C:** ★ A partner runs an agent through `ashe-core` end-to-end in a non-production setting (the Y1 adoption proof). Publish the integration guide + quickstart. +- **B:** Layer-2 design spike: scope the Node.js `fs`/`child_process`/`http` hooks (no implementation yet — design only, to de-risk Y2). +- **A:** Submit the **Internet-Draft (IETF)** or **W3C Community Group** report (Stretch). + +### M11 — 2027-05 · *Standardization motion* +- **A:** Socialize the I-D/CG; seek a second independent reviewer of the spec; begin building the case for a working group (needs demonstrated multi-vendor interest). +- **C:** First conference talk delivered or accepted; open the community forum. +- **D:** Confirm Y2 funding (grant secured or foundation host committed). + +### M12 — 2027-06 · *Year-1 exit* 🟢 +- **Gate 🟢 (Y1 exit):** Spec v1.0 frozen ✓; `ashe-core` passes both conformance gates in CI ✓; ≥1 external E2E run ✓ (Target); ≥1 grant or foundation host ✓ (Target). **If funding gate fails → trigger §15 contingency (lower-burn track).** +- **Metrics check:** 2 design partners; 1 integration spike; spec v1.0; 1 grant/host; 50+ stars, 5+ external contributors. +- **Re-plan:** expand Year 2 to task level for the M13 start. + +--- + +## Year 2 — *Running code, independently* (M13–M24, 2027-07 → 2028-06) + +> Theme: cross the **multiple-independent-implementations** bar; Layer-2 mediation via the Node.js SDK; the first interop event. **Entry 🟢:** Y1 exit passed. + +| Month | Primary milestone | Track | Marker | +|---|---|---|---| +| M13 (2027-07) | Layer-2 Node hooks begin: `fs` interception routed through ASHE for ASHE-aware processes | B | | +| M14 (2027-08) | Conformance suite v1.0 released as the authoritative interop bar (W/H/N/R + functional + Layer-2 profile) | A | ★ | +| M15 (2027-09) | `child_process` + `http` hooks; honest claim moves to "all SDK code is mediated" · Q-review | B | 🟢 | +| M16 (2027-10) | Second-implementation steward reaches first passing conformance run | C | ★ keystone-in-progress | +| M17 (2027-11) | Tier-2 evaluators (LLM-backed where the graph calls for it; tiny-ONNX default elsewhere) | B | | +| M18 (2027-12) | Continuum open-core split lands: Apache-2.0 core vs first commercial surface (managed audit + hosted broker) · half-year funding review | B/D | | +| M19 (2028-01) | First interop event (hackathon-style): implementations test against each other + the suite | A | ★ (Target) | +| M20 (2028-02) | Spec v1.1 — errata/clarifications from independent implementers (the highest-value feedback) | A | | +| M21 (2028-03) | First site operators publish `.well-known/ashe` (Element 13 / ADR-018) · Q-review | C | 🟢 | +| M22 (2028-04) | TSC seated with ≥1 non-Phor seat; no-single-org-majority rule in force | E | 🟢 | +| M23 (2028-05) | Continuum first paying design partner (Target); 18-month forward runway secured | D | | +| M24 (2028-06) | **Y2 exit 🟢:** ≥2 independent conformant impls ★; Layer-2 shipped; open-core split + ($ or grant); TSC seated | all | 🟢★ | + +> **Y2 is the hinge year.** The keystone metric — *the second independent conformant implementation* — is the line between "a well-documented project" and "a real standard." If M16/M24 slip, re-examine the adoption thesis (ROADMAP §15 R2) before proceeding. + +--- + +## Year 3 — *Multi-language, multi-deployment, compliance-credible* (M25–M36, 2028-07 → 2029-06) + +> Theme: the protocol is real in four languages; enterprises can map it to compliance frameworks; the standards-track motion matures. **Entry 🟢:** Y2 exit (esp. ≥2 conformant impls). + +| Month | Primary milestone | Track | Marker | +|---|---|---|---| +| M25 (2028-07) | Python SDK begins (LangChain/AutoGen/CrewAI userbase) | B | | +| M26 (2028-08) | Python SDK passes conformance; Rust SDK begins (path to Layer 3) | B | | +| M27 (2028-09) | Compliance mappings published: SOC 2 / ISO 27001 / HIPAA + FedRAMP-readiness narrative · Q-review | A | 🟢 | +| M28 (2028-10) | Rust SDK passes conformance; Go SDK begins (cloud-native) | B | | +| M29 (2028-11) | Validation-graph maturation: standalone engine hardened; intent-vs-action node production-grade | B | | +| M30 (2028-12) | Go SDK passes conformance → **4-language conformant matrix (TS/Python/Rust/Go)** · half-year funding review | B | ★ | +| M31 (2029-01) | Pursue WG charter (IETF/W3C) — *or* document the de-facto-path pivot if a formal WG isn't reachable | A | 🔺 (Stretch) | +| M32 (2029-02) | Spec v2.0 — Layer-2 lessons + multi-language results; defines the Layer-3 profile ahead of impl | A | | +| M33 (2029-03) | First regulated-industry pilot (fintech/health/gov) live on Continuum enterprise · Q-review | C | 🟢 | +| M34 (2029-04) | **Layer-3 spike** (gVisor/Firecracker/eBPF feasibility vs the spec) | B | 🔺 go/no-go | +| M35 (2029-05) | Cross-org capability-federation pilot (one org's tokens accepted by another under policy) | C | | +| M36 (2029-06) | **Y3 exit 🟢:** 4-lang SDKs; compliance mappings; regulated pilot; Layer-3 spike decision; WG charter or de-facto pivot | all | 🟢 | + +--- + +## Year 4 — *Adversarial-grade enforcement; production at scale* (M37–M48, 2029-07 → 2030-06) + +> Theme: Layer-3 runtime mediation in production — the first point ASHE bounds the blast radius of *uncooperative* code, not just cooperating code. **Entry 🟢:** Y3 exit; Layer-3 spike = "go." + +| Month | Primary milestone | Track | Marker | +|---|---|---|---| +| M37 (2029-07) | Layer-3 mediation build begins: eBPF (Linux) syscall mediation | B | | +| M38 (2029-08) | Sandbox integration (gVisor / Firecracker) for controlled processes | B | | +| M39 (2029-09) | Layer-3 conformance profile finalized + added to the suite (syscall mediation, blast-radius bounding) · Q-review | A | 🟢 | +| M40 (2029-10) | Continuum production hardening: HA broker, revocation at scale | B/D | | +| M41 (2029-11) | WEIGHTLESS budget validated under real load (ADR-015 benchmark publication) | A | ★ | +| M42 (2029-12) | Layer-3 GA; honest claim: "syscalls from controlled processes go through ASHE; blast radius bounded by capability set" · half-year review | B | ★ | +| M43 (2030-01) | Independent security review commissioned | A | | +| M44 (2030-02) | Interop event #2/#3; cross-implementation Layer-2 interop demonstrated publicly | A | | +| M45 (2030-03) | 3–5 enterprise production deployments at Layer-2/3 · Q-review | C | 🟢 | +| M46 (2030-04) | **Layer-4 research** (TPM/TEE attestation feasibility) | B | 🔺 go/no-go | +| M47 (2030-05) | Independent security review **published**; remediations landed | A | ★ | +| M48 (2030-06) | **Y4 exit 🟢:** Layer-3 GA + 3+ prod deployments; security review published; ≥50% sustainability from revenue | all | 🟢 | + +--- + +## Year 5 — *De facto standard; hardware-rooted pilots* (M49–M60, 2030-07 → 2031-06) + +> Theme: ASHE is the protocol people reach for when an agent needs bounded authority — ratified or de-facto — with hardware-anchored enforcement available for the highest stakes. **Entry 🟢:** Y4 exit. + +| Month | Primary milestone | Track | Marker | +|---|---|---|---| +| M49 (2030-07) | Layer-4 hardware-rooted pilot begins (TPM/TEE; hardware-verified capability tokens) | B | | +| M50 (2030-08) | Conformance certification program operational (3rd parties certify "ASHE-conformant, Layer N") | A | ★ | +| M51 (2030-09) | Layer-4 pilot demonstrated for a highest-stakes deployment · Q-review | B | 🟢 | +| M52 (2030-10) | Spec v3.0 — the mature multi-layer standard | A | ★ | +| M53 (2030-11) | Broaden cross-vendor adoption: multiple agent vendors + frameworks shipping ASHE | C | | +| M54 (2030-12) | Self-sustaining funding confirmed; no single funder >~25% (capture guardrail) · half-year review | D | 🟢 | +| M55 (2031-01) | Ratification submission matured (RFC / W3C Recommendation track) — or de-facto criteria audit | A | 🔺 (Stretch / north star) | +| M56 (2031-02) | ASHE appears in enterprise procurement / agent-security RFPs as an expected capability | C | ★ (Stretch) | +| M57 (2031-03) | Continuum offers full enforcement spectrum (Layers 1→4); flagship-not-sole conformant impl · Q-review | B | 🟢 | +| M58 (2031-04) | External contributor community larger than the funded team (the "escaped its origin" marker) | E | ★ | +| M59 (2031-05) | Final ratification push / de-facto-status documentation | A | | +| M60 (2031-06) | **Y5 exit 🟢 ("have we won?"):** ≥5 impls (3 in prod ≥L2); ratified or de-facto; Layer-4 pilot; self-sustaining | all | 🟢★ | + +--- + +## How to use this document + +- **Operate from the current year only.** Years 2–5 are milestone-level on purpose; each annual exit gate (🟢) re-plans the next 12 months to task level, against reality. +- **Gates are real stops.** A 🟢 that fails does not roll forward silently — it triggers the matching ROADMAP §14 gate action (extend, pivot to de-facto path, or trigger the funding contingency). +- **The two numbers that matter most:** the *second independent conformant implementation* (M16→M24) and *self-sustaining-with-neutrality* (M54). Standard-ness and survival, respectively. If either is red, it outranks everything else that month. +- **Slippage is expected and logged, not hidden** — consistent with the corpus's honesty discipline (ADR-014/015). Calendar dates are planning anchors; the *sequence* and the *gates* are the commitment. diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..a62699b --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,429 @@ +# ASHE — Long-Range Roadmap (Inception → Year 5) + +> *Open standard, grant- and sponsorship-funded, built to become the de facto cross-vendor capability-broker protocol for the agent era. Continuum (the commercial reference implementation) exists to sustain and accelerate adoption — not the other way around.* + +--- + +## 0. How to read this document + +This is the program-level operating plan for ASHE from inception through a five-year horizon. It is **granular by design** — quarter-level deliverables, explicit go/no-go gates, metrics with targets, a funding plan, an org plan, and a risk register. + +**Posture (decided 2026-06):** + +| Axis | Decision | Consequence for this plan | +|---|---|---| +| **Business model** | Open-core — ASHE is Apache-2.0; Phor monetizes Continuum (managed broker, enterprise enforcement, compliance packs) atop it | Commercial milestones are *subordinate* to adoption milestones; Continuum revenue is a sustainability input, not the north star | +| **Funding** | Grant / standards-body funded | Burn is capital-efficient; hiring is lean and standards/eng-weighted; milestones gate on grant cycles and ratification, not on a VC growth curve | +| **North star (Y5)** | De facto cross-vendor standard | When adoption and revenue trade off, adoption wins. Ubiquity, independent implementations, and ratification are the primary scoreboard | + +**Epistemic discipline.** Per [ADR-015](decisions/ADR-015-validation-methodology-and-tiered-claims.md), every forward target here is a **planning target**, not a commitment, and is graded: + +- **(Floor)** — high-confidence, within our control (e.g., "publish spec v1.0"). +- **(Target)** — plausible with normal execution and funding (e.g., "3 independent conformant implementations"). +- **(Stretch)** — requires external alignment we don't control (e.g., "IETF WG charter adopted"). + +**Calendar anchor.** Program Year 1 (Y1) begins **2026 H2** (inception, now). Years run on program quarters: Y1Q1 = 2026 Q3 … Y5Q4 = 2031 Q2. Quarters are planning units; calendar slippage is expected and gated, not fatal. + +**Relationship to the corpus.** This roadmap *operationalizes* decisions already made: the four-layer enforcement trajectory ([ADR-014](decisions/ADR-014-phased-enforcement-model.md)), deployment profiles ([ADR-009](decisions/ADR-009-deployment-profiles.md)), validation methodology ([ADR-015](decisions/ADR-015-validation-methodology-and-tiered-claims.md)), the conformance gate ([ADR-020](decisions/ADR-020-weightlessness-proper-application-conformance.md)), the tri-surface architecture (agent/dev/web — [ADR-014](decisions/ADR-014-phased-enforcement-model.md)/[ADR-017](decisions/ADR-017-sealed-workspace-foundational-dev-pattern.md)/[ADR-018](decisions/ADR-018-well-known-ashe-web-side-interaction-point.md)), and the first-slice cohesion sequence ([ADR-005](decisions/ADR-005-first-slice-cohesion.md)). It does not introduce new architecture; it schedules the existing one and adds the standardization, funding, governance, and org tracks the ADRs presuppose but do not specify. + +--- + +## 1. Strategy — why standardization-first + +The thesis of the whole effort (from [MANIFESTO.md](MANIFESTO.md) / [VISION.md](VISION.md)): **capability mediation between agents and software should be a public cross-vendor protocol, not a single-vendor advantage.** A standard wins by being adopted, and a protocol standard specifically wins by clearing three hurdles in order: + +1. **Credible specification** — a spec precise enough that two parties who never talked can interoperate. (We are here, at v0.x.) +2. **Multiple independent implementations** — the IETF "rough consensus and running code" bar; the single strongest signal that a spec is real and not vendor fiction. +3. **Neutral governance + adoption flywheel** — no single vendor controls it; agent vendors and site operators adopt it because *other* agent vendors and site operators have. + +The grant/standards funding posture is chosen *because* it serves hurdle 3: a protocol owned by a VC-backed company racing to revenue is structurally suspected of capture (the [CASE-FOR-NOW §1.8.1](CASE-FOR-NOW.md) "motivated rejection" problem). A protocol stewarded by a neutral foundation, funded by grants and sponsorship, with an open-core commercial implementation among several, is the configuration that has historically won standards races (TCP/IP, HTTP, TLS, OAuth, Let's Encrypt/ACME). + +**Continuum's role.** Continuum is the reference implementation and the proving ground. It is open-core: the protocol and a conformant core are Apache-2.0; enterprise enforcement (managed broker, Layer 3/4 operations, compliance evidence packs, support/SLA) is commercial. Continuum funds the foundation's sustainability and gives the spec a flagship adopter — but the plan deliberately *invites and resources competing implementations*, because hurdle 2 cannot be met by Continuum alone. + +**The flywheel we are building:** + +``` + grants + sponsorship + │ + ▼ + credible spec + conformance suite ──► independent implementations + ▲ │ + │ ▼ + neutral governance ◄──── adoption (agent vendors + site operators) + │ │ + └────────── ratification ◄─────────────┘ + │ + ▼ + de facto standard (Y5 north star) +``` + +--- + +## 2. The three tracks + +Everything below is organized into three parallel tracks plus two enabling tracks. Each year section reports deliverables per track. + +| Track | Owns | Win condition | +|---|---|---| +| **A. Standard** | The specification, the conformance suite, governance, the standards-body relationship, interop | ASHE is a ratified or de-facto cross-vendor standard with neutral governance | +| **B. Implementation** | The reference implementation (Continuum core, Apache-2.0), the SDKs, the four-layer enforcement trajectory | A conformant, production-grade reference impl exists at each enforcement layer on schedule | +| **C. Adoption** | Design partners, agent-vendor integrations, site-operator `.well-known/ashe` deployments, devrel, ecosystem | Independent parties ship ASHE in production because it's worth it to them | +| **D. Sustainability** (enabling) | Grants, sponsorship, the foundation, Continuum open-core revenue | The effort is funded through Y5 without compromising neutrality | +| **E. Org & Governance** (enabling) | People, hiring, IP/patent posture, decision process | A lean team and a capture-resistant governance structure | + +--- + +## 3. Phase model — version ↔ enforcement layer ↔ year + +Anchored to [ADR-014](decisions/ADR-014-phased-enforcement-model.md)'s committed trajectory. **Each layer transition is its own discrete project with an explicit go-ahead gate** (ADR-014 implementation note) — the plan never commits a later layer's work as a precondition for an earlier layer's value. + +| Program year | ASHE version | Enforcement layer reached | Honest claim ceiling (ADR-014) | Standard-track milestone | +|---|---|---|---|---| +| **Inception (Y0/Y1Q1)** | v0 (done) | Layer 1 token issuance + scope-check; conformance scaffold ([ADR-020](decisions/ADR-020-weightlessness-proper-application-conformance.md)) green | "ASHE exists in code form" — not a security claim | Spec v0.x published; conformance gate v0 runnable | +| **Year 1** | v1 | Layer 1 fully active; cooperating IPC gated; intent reconciliation evaluators | "Cooperating traffic is gated; bypassing the IPC layer bypasses ASHE" | Spec v1.0 frozen; Internet-Draft / W3C CG submitted *(Stretch)* | +| **Year 2** | v2 | Layer 2 via Node.js SDK adapters; tier-2 evaluators | "All code using the ASHE SDK is mediated" | Conformance suite v1; first interop event; ≥3 independent impls *(Target)* | +| **Year 3** | v3 | Layer 2 across Python/Rust/Go SDKs; broadened evaluators | (as v2, multi-language) | WG charter / standards-track adoption *(Stretch)*; compliance mappings | +| **Year 4** | v4–v5 | Layer 3 runtime mediation (gVisor/Firecracker/eBPF) | "Syscalls from controlled processes go through ASHE; blast radius bounded by capability set" | Standards-track maturation; Layer-3 conformance profile | +| **Year 5** | v6 (pilot) | Layer 4 hardware-rooted (TPM/TEE attestation) pilots | "Mediation is hardware-anchored; capability boundaries cryptographically enforced" | De facto / ratified standard; multi-vendor production at scale | + +--- + +## 4. Inception — Now (Y0 → Y1Q1, 2026 H2) + +**Theme:** *Turn a credible set of documents into a credible, runnable protocol with a conformance bar.* + +**Where we actually are (baseline, verified):** + +- Spec corpus published v0.x: MANIFESTO, VISION, CASE-FOR-NOW, WEIGHTLESS, ADRs 001–020. +- The conformance gate is **real and runnable**: [ADR-020](decisions/ADR-020-weightlessness-proper-application-conformance.md) + the `conformance/` suite (adapter contract, language-neutral manifest, four W/H/N/R test groups, CI green). +- The reference protocol primitives exist in embryo: object-capability core (unforgeable capabilities, attenuate-only sets), structural mediation, tamper-evident audit, lease issuance, intent reconciliation — all unit-tested. + +**Inception deliverables (this is the work in flight):** + +| Track | Deliverable | Grade | +|---|---|---| +| A | ADR-020 conformance gate + runnable suite; CI on every PR | Floor (done) | +| A | `SPEC.md` skeleton — extract the normative wire contract from VISION/ADRs into a single citable spec document with section numbering | Floor | +| B | Reference protocol primitives → a coherent `ashe-core` package (capability, lease, mediation, audit, intent) with a public API surface | Floor | +| B | Lease **revocation lifecycle** + the slow-path/fast-path split (the one honest gap WEIGHTLESS.md names) | Floor | +| C | Design-partner shortlist (3–5 agent vendors or agent-framework maintainers) + a one-page integration pitch | Target | +| D | Grant prospect list + first two applications drafted (see §11) | Floor | +| E | Choose the governance vehicle (new foundation vs. hosted under an existing one) — decision memo | Floor | + +**Inception exit gate (must pass to declare Y1 open):** +- Spec skeleton exists and the conformance suite tests against it. +- `ashe-core` issues a lease, mediates a routine action at literal-zero, mediates a Tier-C action through the explicit boundary, and writes a verifiable audit record — end to end. +- At least one grant application submitted. +- Governance-vehicle decision made. + +--- + +## 5. Year 1 (2026 Q3 – 2027 Q2) — *Freeze v1; plant the standard* + +**Theme:** *A frozen, implementable spec v1.0; a Layer-1 reference implementation that a second party can independently re-implement; the standardization motion formally begun.* + +**Entry gate:** inception exit gate passed. + +### Workstreams + +**Track A — Standard** +- Y1Q1: `SPEC.md` v0.9 — complete normative surface: capability grammar, lease lifecycle, intent declaration, audit record format, the wire contract (Protobuf canonical + JSON/TOON projections per [ADR-012](decisions/ADR-012-wire-format-grpc-protobuf-with-projections.md)/[ADR-006](decisions/ADR-006-toon-dual-projection.md)), and the four conformance profiles tied to [ADR-014](decisions/ADR-014-phased-enforcement-model.md) layers. +- Y1Q2: Public RFC period on v0.9 (GitHub-based, 8 weeks); intake and disposition of comments. +- Y1Q3: **Spec v1.0 freeze** *(Floor)* — semantic-versioned, change-controlled. v1.0 commits the protocol *shape* that lets Layers 2–4 be added without rewrite (ADR-014). +- Y1Q4: Submit an **Internet-Draft (IETF)** or **W3C Community Group** report *(Stretch — depends on external uptake)*; publish the conformance suite v1 spec (the weightlessness gate plus a *functional* gate: lease, scope, audit, intent correctness). + +**Track B — Implementation (v1, Layer 1 fully active)** +- Y1Q1: `ashe-core` (TypeScript) — production-shaped: lease issuance/validation/revocation, the interception-chain mediation point ([ADR-007](decisions/ADR-007-interception-chain-pattern.md)), the validation graph engine scaffold ([ADR-008](decisions/ADR-008-validation-graph-tiny-onnx.md)/[ADR-010](decisions/ADR-010-standalone-graph-engine.md)). +- Y1Q2: Cooperating IPC gating — the ASHE broker gates cooperating traffic; bypassing the IPC layer bypasses ASHE (the honest v1 claim). +- Y1Q3: Intent-reconciliation evaluators (tier-1, deterministic + tiny-ONNX default per ADR-008). +- Y1Q4: `ashe workspace init` MVP ([ADR-017](decisions/ADR-017-sealed-workspace-foundational-dev-pattern.md)) — the dev-side surface, wall-up-first; DevContainer adapter as first isolation substrate. + +**Track C — Adoption** +- Y1Q2: 2 design partners signed (LOI / informal) — ideally one agent-framework maintainer and one site operator. +- Y1Q3: First external integration spike — a partner runs an agent through `ashe-core` in a non-production setting. +- Y1Q4: Publish an integration guide + a 15-minute "hello, capability lease" quickstart; first conference talk submitted. + +**Track D — Sustainability** +- Y1Q1–Q2: Submit 3–4 grant applications (§11). Target close: ≥1 grant by Y1Q4 *(Target)*. +- Y1Q3: Stand up the governance vehicle (foundation or hosted project); publish the governance charter and IP policy. + +**Track E — Org** +- Hire/retain: protocol editor (could be PK), 1 reference-impl engineer, fractional devrel. Keep ≤3 FTE-equivalent. (See §12.) + +**Exit gate (Y1 → Y2):** +- Spec v1.0 frozen and change-controlled. *(Floor)* +- `ashe-core` passes both conformance gates (weightlessness + functional) in CI. *(Floor)* +- ≥1 external party has run ASHE end-to-end. *(Target)* +- ≥1 grant secured **or** a foundation host committed. *(Target — if neither, trigger the funding-contingency in §15)* + +**Y1 metric targets:** 2 design partners; 1 independent integration spike; spec v1.0; 1 grant or host; GitHub: 50+ stars, 5+ external contributors (Target). + +--- + +## 6. Year 2 (2027 Q3 – 2028 Q2) — *Running code, independently* + +**Theme:** *Cross the "multiple independent implementations" bar; Layer-2 mediation via the Node.js SDK; the first interop event.* + +**Entry gate:** Y1 exit gate passed. + +### Workstreams + +**Track A — Standard** +- Conformance suite **v1.0 released** as the authoritative interop bar (expands [ADR-020](decisions/ADR-020-weightlessness-proper-application-conformance.md)'s W/H/N/R groups with functional + Layer-2 profiles). +- **First interop event** (IETF-hackathon-style or a hosted ASHE interop) — implementations test against each other and the suite *(Target)*. +- Spec v1.1 — errata + clarifications surfaced by independent implementers (the single most valuable spec feedback there is). + +**Track B — Implementation (v2, Layer 2 via Node.js)** +- Node.js runtime hooks: route `fs`, `child_process`, `http` through ASHE for ASHE-aware processes ([ADR-014](decisions/ADR-014-phased-enforcement-model.md) v2 note). Honest claim moves to "all code using the ASHE SDK is mediated." +- Tier-2 evaluators (LLM-backed where the validation graph calls for it; default-to-tiny-ONNX otherwise). +- Continuum **open-core split** lands: Apache-2.0 core vs. the first commercial surface (managed audit + hosted broker) — see Track D. + +**Track C — Adoption** +- **≥3 independent conformant implementations** in progress, ≥2 passing the suite *(Target — the keystone Y2 outcome)*. At least one must be non-Continuum (a partner's, a community impl, or a second-language port stewarded separately). +- 5+ agent-vendor or framework integrations (Target); first site operators publish `.well-known/ashe` ([ADR-018](decisions/ADR-018-well-known-ashe-web-side-interaction-point.md)). +- Devrel: docs site, reference quickstarts in 2 languages, an ASHE community forum. + +**Track D — Sustainability** +- Continuum commercial v1: managed broker + hosted tamper-evident audit (the `audit.ts` hash-chain productized) + SLA support. First paying design partner *(Target)*. +- Second grant cycle / renew sponsorship; aim for 18-month forward runway. + +**Track E — Org** +- Grow to ~4–6 FTE-equiv: +1 conformance/interop engineer, +1 ecosystem/partnerships, +fractional standards counsel. Establish a Technical Steering Committee (TSC) with ≥1 non-Phor seat. + +**Exit gate (Y2 → Y3):** +- ≥2 independent implementations pass conformance suite v1. *(Target — if not met, this is the critical risk; see §15 "adoption chicken-and-egg")* +- Layer-2 Node SDK shipped and conformant. *(Floor)* +- Continuum open-core split shipped; ≥1 paying customer **or** a renewed grant. *(Target)* +- TSC seated with external representation. *(Floor)* + +**Y2 metric targets:** 3 impls (2 conformant); 5 integrations; 3 `.well-known/ashe` deployments; first revenue or grant renewal; 250+ stars, 20+ contributors. + +--- + +## 7. Year 3 (2028 Q3 – 2029 Q2) — *Multi-language, multi-deployment, compliance-credible* + +**Theme:** *The protocol is real in four languages; enterprises can map it to their compliance frameworks; the standards-track motion matures.* + +**Entry gate:** Y2 exit gate passed (especially ≥2 conformant impls). + +### Workstreams + +**Track A — Standard** +- Pursue a **working-group charter** (IETF WG or W3C WG) *(Stretch — gated on demonstrated multi-vendor interest)*; if a formal WG isn't reachable, consolidate the **de-facto** path: a stable spec + dominant conformance suite + plural implementations. +- Spec v2.0 — incorporates Layer-2 lessons and the multi-language conformance results; defines the Layer-3 profile ahead of implementation. +- Publish **compliance mappings**: ASHE audit + capability discipline → SOC 2, ISO 27001, HIPAA, and a FedRAMP-readiness narrative (per [ADR-017](decisions/ADR-017-sealed-workspace-foundational-dev-pattern.md) consequences). + +**Track B — Implementation (v3, Layer 2 across languages)** +- SDKs: **Python** (LangChain/AutoGen/CrewAI userbase), **Rust** (path to Layer 3, per ADR-014 priority), **Go** (cloud-native). Each passes conformance. +- Validation-graph maturation ([ADR-008](decisions/ADR-008-validation-graph-tiny-onnx.md)/[ADR-010](decisions/ADR-010-standalone-graph-engine.md)): standalone engine, composable evaluators, the intent-vs-action reconciliation node hardened. +- Begin **Layer-3 spike** (discrete project, own gate): gVisor/Firecracker/eBPF feasibility against the spec. + +**Track C — Adoption** +- 15+ integrations; 25+ `.well-known/ashe` deployments (Target); first regulated-industry pilot (fintech/health/gov) on Continuum enterprise. +- Cross-org capability federation pilot (one org's capability tokens accepted by another under policy) — the [ADR-017](decisions/ADR-017-sealed-workspace-foundational-dev-pattern.md) "what becomes possible" item. + +**Track D — Sustainability** +- Continuum enterprise: compliance evidence packs, role-template libraries, Layer-2 managed enforcement. Revenue covers a growing share of foundation sustainability (Target: ≥30%). +- Sustained sponsorship program (corporate members of the foundation). + +**Track E — Org** +- ~7–10 FTE-equiv. Per-language maintainer model (some community, some funded). Formal contribution governance (RFC process, maintainer guidelines, CoC). + +**Exit gate (Y3 → Y4):** +- 4-language conformant SDK matrix (TS/Python/Rust/Go). *(Target)* +- Compliance mappings published; ≥1 regulated pilot live. *(Target)* +- Layer-3 spike produces a go/no-go decision with evidence. *(Floor — the decision is the deliverable, not necessarily "go")* +- Standards-track motion: WG charter *(Stretch)* **or** an explicit, documented de-facto-path pivot. *(Floor)* + +**Y3 metric targets:** 4 SDKs; 15 integrations; 25 site deployments; ≥30% sustainability from revenue; 750+ stars, 50+ contributors. + +--- + +## 8. Year 4 (2029 Q3 – 2030 Q2) — *Adversarial-grade enforcement; production at scale* + +**Theme:** *Layer 3 runtime mediation in production — the first point at which ASHE bounds the blast radius of uncooperative code, not just cooperating code.* + +**Entry gate:** Y3 exit gate passed; Layer-3 spike = "go." + +### Workstreams + +**Track A — Standard** +- Layer-3 **conformance profile** finalized and added to the suite (syscall-mediation correctness, blast-radius bounding tests). +- Spec v2.x maturation toward standards-track stability; independent security review / formal-ish analysis of the core protocol commissioned and published. +- Interop event #2/#3; cross-implementation Layer-2 interop demonstrated publicly. + +**Track B — Implementation (v4–v5, Layer 3)** +- Layer-3 runtime mediation shipped: eBPF (Linux), sandbox integration (gVisor/Firecracker). Honest claim: "syscalls from controlled processes go through ASHE; vulnerability impact bounded to the capability set." +- Continuum production hardening: HA broker, performance at scale (validate the WEIGHTLESS budget under real load per [ADR-015](decisions/ADR-015-validation-methodology-and-tiered-claims.md)), revocation at scale. +- Begin **Layer-4 research** (TPM/TEE attestation, signed capability tokens) — discrete, own gate. + +**Track C — Adoption** +- Enterprise production deployments (Target: 5–10 organizations running ASHE Layer-2/3 in production). +- A frontier or major agent vendor ships an ASHE integration in a shipping product *(Stretch — a tipping-point signal)*. +- 50+ `.well-known/ashe` site deployments; an ASHE-aware tool ecosystem (editors, CI, deploy tools per ADR-017 adapter priorities). + +**Track D — Sustainability** +- Continuum revenue covers ≥50% of sustainability (Target); diversified grants/sponsorship cover the neutral-governance remainder. +- Endowment-style runway: ≥24 months forward funding secured. + +**Track E — Org** +- ~10–15 FTE-equiv. Security engineering capacity for Layer-3. Foundation operations formalized (membership dues, events, conformance certification program). + +**Exit gate (Y4 → Y5):** +- Layer-3 shipped and conformant; ≥3 production deployments at Layer-2/3. *(Target)* +- Independent security review published. *(Floor)* +- Sustainability ≥50% non-grant-dependent **or** multi-year grant secured. *(Target)* + +**Y4 metric targets:** Layer-3 GA; 5–10 production orgs; published security review; 50+ site deployments; ≥50% revenue sustainability; 1500+ stars. + +--- + +## 9. Year 5 (2030 Q3 – 2031 Q2) — *De facto standard; hardware-rooted pilots* + +**Theme:** *ASHE is the protocol people reach for when an agent needs bounded authority — ratified or de-facto — with hardware-anchored enforcement available for the highest-stakes deployments.* + +**Entry gate:** Y4 exit gate passed. + +### Workstreams + +**Track A — Standard** +- **Ratification or documented de-facto status** *(Stretch / north star)*: either a published RFC / W3C Recommendation, or the demonstrable de-facto position — stable spec, dominant conformance suite, 5+ independent implementations, broad production adoption, neutral governance. +- Spec v3.0 — the mature, multi-layer standard. Conformance certification program operational (third parties can certify "ASHE-conformant, Layer N"). + +**Track B — Implementation (v6 pilot, Layer 4)** +- Layer-4 hardware-rooted pilots: TPM/TEE attestation, hardware-verified capability tokens, for the highest-stakes deployments. Honest claim: "mediation hardware-anchored; capability boundaries cryptographically enforced; trust base = hardware root + protocol implementation." +- Continuum: full enforcement-layer spectrum (1→4) offered; the flagship but not sole conformant implementation. + +**Track C — Adoption** +- Cross-vendor ubiquity: multiple agent vendors, multiple frameworks, a meaningful fraction of agent-facing site operators publishing `.well-known/ashe`. +- ASHE referenced in enterprise procurement / agent-security RFPs as an expected capability *(Stretch — the strongest adoption signal there is)*. + +**Track D — Sustainability** +- Self-sustaining: Continuum revenue + foundation membership + targeted grants cover operations with multi-year runway. Neutrality preserved (no single funder >~25% — a capture guardrail). + +**Track E — Org** +- ~15–20 FTE-equiv across foundation + Continuum, with a healthy external maintainer/contributor community larger than the funded team — the real marker of a standard that has escaped its origin. + +**Exit gate (Y5 — "have we won?"):** +- ≥5 independent implementations, ≥3 in production at Layer-2+. *(Target)* +- Ratification achieved **or** de-facto criteria objectively met. *(Stretch — north star)* +- Layer-4 pilot demonstrated. *(Target)* +- Self-sustaining funding with neutrality guardrails intact. *(Target)* + +**Y5 metric targets:** 5+ impls; ratified/de-facto; Layer-4 pilot; self-sustaining; contributor community > funded team. + +--- + +## 10. Governance & IP + +- **License:** ASHE spec + `ashe-core` under **Apache 2.0** (patent grant included — the anti-ambush posture already in [NOTICE](NOTICE)/[LICENSE](LICENSE)). +- **Vehicle:** decide at inception between (a) a new lightweight foundation and (b) hosting under an existing neutral home (Linux Foundation / OpenSSF / Joint Development Foundation for the spec). Default lean: **host under an existing foundation** (faster neutrality credibility, lower ops burden) and graduate to a dedicated entity only if scale demands. *(Floor decision at inception.)* +- **Decision process:** documented RFC/ADR process (the existing ADR discipline extends into governance). A Technical Steering Committee seated by Y2 with mandatory non-Phor representation; a hard rule that **no single organization holds a TSC majority**. +- **Trademark:** "ASHE" and the conformance mark held by the neutral entity, licensed for conformant use — the standard lever for keeping "ASHE-compatible" honest. +- **Capture resistance:** funding-concentration cap (no funder >~25% of operating budget by Y5); open meeting minutes; public conformance results. + +--- + +## 11. Funding plan (grant + sponsorship + open-core) + +**Why this is fundable.** ASHE sits at the intersection of (a) AI safety / agent containment — the [CASE-FOR-NOW](CASE-FOR-NOW.md) weaponization narrative — and (b) open internet/security infrastructure. Both have established philanthropic and public funders. + +| Source class | Specific prospects | Fit | Stage | +|---|---|---|---| +| Open-internet / security infra | Sloan Foundation, Open Technology Fund, NLnet (NGI Zero), Mozilla | "Public-good protocol + running code" is exactly their pattern | Y0–Y2 | +| AI-safety philanthropy | Open Philanthropy, Survival & Flourishing Fund, Longview, AI-safety-oriented funders | Structural agent-containment / bounded-outcomes thesis | Y0–Y3 | +| Public research | NSF (SaTC / cyberinfrastructure), EU Horizon / NGI, national cyber agencies | Standards + reference impl + conformance | Y1–Y4 | +| Foundation hosting / sponsorship | Linux Foundation, OpenSSF corporate members | Neutral home + corporate sponsorship dues | Y1–Y5 | +| Open-core revenue (Continuum) | Enterprise managed broker, compliance packs, support | Sustainability, subordinate to adoption | Y2–Y5 | + +**Funding trajectory (planning, not commitment):** Y0–Y1 seed grant(s) to fund spec freeze + reference impl; Y2 foundation hosting + first revenue; Y3–Y4 diversified grants + growing revenue (→≥50% by Y4); Y5 self-sustaining with the <25%-per-funder neutrality cap. + +**Contingency:** if no grant and no host by end of Y1, fall back to a lower-burn track — PK + 1 engineer, slower layer trajectory, Continuum revenue prioritized earlier for survival — without abandoning the open spec (see §15). + +--- + +## 12. Org & hiring plan (lean, standards/eng-weighted) + +| Phase | FTE-equiv | Roles added | +|---|---|---| +| Inception | 1–2 | Protocol editor (PK), 1 reference-impl engineer (fractional ok) | +| Y1 | ~3 | +1 reference-impl engineer, +fractional devrel | +| Y2 | ~4–6 | +conformance/interop engineer, +ecosystem/partnerships, +fractional standards counsel; seat the TSC | +| Y3 | ~7–10 | per-language maintainers (mix funded/community), +compliance specialist | +| Y4 | ~10–15 | +security engineering (Layer 3), +foundation ops | +| Y5 | ~15–20 | steady-state foundation + Continuum split; external contributors > funded team | + +Principle: **fund the conformance suite, the spec, and devrel before funding breadth of features.** A standard is won by interop and adoption capacity, not by feature count. + +--- + +## 13. Metrics & KPIs + +Adoption-weighted, because adoption is the north star. Reported quarterly. + +| KPI | Y1 | Y2 | Y3 | Y4 | Y5 | +|---|---|---|---|---|---| +| Independent implementations (any pass conformance) | 1 (ref) | 3 (2 conformant) | 4+ | 5+ | 5+ (3 in prod) | +| Enforcement layer (reference impl) | 1 | 2 | 2 (4 langs) | 3 | 4 (pilot) | +| Agent-vendor / framework integrations | 1 | 5 | 15 | 30 | broad | +| `.well-known/ashe` site deployments | 0 | 3 | 25 | 50+ | meaningful fraction | +| Enterprise production orgs | 0 | 0–1 | 1 (pilot) | 5–10 | scale | +| Sustainability from revenue | 0% | first $ | ≥30% | ≥50% | self-sustaining | +| Spec status | v1.0 | v1.1 | v2.0 | v2.x | v3.0 ratified/de-facto | +| Community (contributors) | 5 | 20 | 50 | 100+ | > funded team | + +**The single most important metric is the second independent conformant implementation (Y2).** Everything before it is preparation; everything after it is a real standard. If one number is red, it's that one. + +--- + +## 14. Gate summary (go/no-go decision points) + +| Gate | When | Pass condition | If failed | +|---|---|---|---| +| Inception exit | Y1Q1 | E2E lease→mediate→audit works; spec skeleton; 1 grant submitted; governance vehicle chosen | Extend inception; do not open Y1 | +| Y1 exit | Y1Q4 | Spec v1.0 frozen; ref impl passes both gates; 1 external E2E run; grant or host | Trigger funding contingency (§15) | +| Y2 exit | Y2Q4 | ≥2 conformant impls; Layer-2 shipped; open-core split + ($ or grant); TSC seated | **Critical** — re-examine adoption thesis (§15) | +| Layer-3 spike | Y3 | Feasibility evidence → explicit go/no-go | If no-go, hold at Layer 2; standard can still win | +| Y3 exit | Y3Q4 | 4-lang SDKs; compliance mappings; regulated pilot; WG charter or de-facto pivot | Pivot to de-facto path | +| Layer-4 research | Y4 | Attestation feasibility → go/no-go | If no-go, Layer 3 is the ceiling; acceptable | +| Y4 exit | Y4Q4 | Layer-3 GA; 3+ prod deployments; security review; ≥50% sustainability | Slow Layer-4; consolidate | +| Y5 "won?" | Y5Q4 | 5+ impls; ratified/de-facto; Layer-4 pilot; self-sustaining | Iterate; the standard race is long | + +--- + +## 15. Risk register + +| # | Risk | Likelihood × Impact | Mitigation | +|---|---|---|---| +| R1 | **Competing standard captures the layer** — MCP extends into capability mediation, or a frontier vendor ships proprietary agent-authorization first | High × High | Position ASHE as *composing above* MCP/auth.md (already the stated posture, [README](README.md)); move fast to the conformance bar; recruit the very vendors who'd otherwise build proprietary; neutral governance as the differentiator they can't match | +| R2 | **Adoption chicken-and-egg** — no second implementation because no adoption, no adoption because no second implementation | High × High | Fund/steward the second implementation directly (Y2 keystone); make the SDK + conformance suite so good that re-implementing is cheap; design-partner LOIs early | +| R3 | **Funding shortfall** — grants don't land, sustainability lags | Medium × High | Diversified prospect list (§11); Y1 contingency (lower-burn, revenue-earlier); never let runway drop below the next gate | +| R4 | **Governance-capture suspicion** — perceived as a Phor land-grab, killing neutral adoption | Medium × High | Neutral foundation host early; non-Phor TSC majority guardrail; funder-concentration cap; open everything (minutes, conformance, decisions) | +| R5 | **Layer-3 complexity** — runtime mediation proves harder/slower than planned | Medium × Medium | It's a discrete gated project (ADR-014); the standard can win at Layer 2; Layer 3 is value-add, not a precondition | +| R6 | **Key-person concentration** (PK) | Medium × High | Document everything (this corpus already does); seat a TSC; grow maintainers; the ADR discipline is the bus-factor insurance | +| R7 | **Overclaim / credibility loss** — a layer's claims get conflated, hostile readers pounce | Medium × High | ADR-014/015 honesty discipline is mandatory; per-layer claim ceilings; published evidence grades; commissioned security review (Y4) | +| R8 | **Spec churn** — v1 freezes the wrong shape, forcing a rewrite | Low × High | v1.0 commits only the *shape* that admits Layers 2–4 without rewrite (ADR-014); independent implementers stress the spec early (Y2 interop) | + +--- + +## 16. Sequencing logic (why this order) + +1. **Spec freeze before SDK breadth** — you cannot ask a second party to implement a moving target. v1.0 freeze (Y1) gates the independent-implementation push (Y2). +2. **Conformance suite before adoption push** — adoption without a conformance bar produces incompatible forks, which kills a standard. The suite (started at inception via ADR-020) is the spine. +3. **Layer 1 → 2 → 3 → 4, never skipped** — ADR-014's explicit rejection of "jump to Layer 3." Each layer validates the protocol shape at lower cost before the next layer's investment. +4. **Standardization motion after running code, not before** — IETF/W3C bodies adopt rough consensus *and running code*; submitting a draft (Y1Q4) only after the reference impl and conformance suite exist is what makes it credible rather than vaporware. +5. **Revenue subordinate to adoption** — open-core commercial surfaces (Y2+) are timed to *fund* the adoption flywheel, never to fence it. + +--- + +## 17. What success and failure look like at Year 5 + +**Success:** an agent vendor adding capability mediation reaches for ASHE the way a web service reaches for TLS — because it's the neutral, well-specified, multiply-implemented, conformance-tested standard, with hardware-anchored enforcement available when the stakes demand it. Continuum is a healthy commercial implementation among several. The foundation is self-sustaining and uncaptured. + +**Acceptable partial win:** ASHE is the de-facto standard in a meaningful niche (e.g., agentic dev workflows via the sealed-workspace surface) even if not universal; Layer 3 is the production ceiling; the spec and conformance suite are the reference others build against. + +**Failure modes to detect early:** the second implementation never appears (R2 — detect by end of Y2); a proprietary competitor reaches ubiquity first (R1 — detect by Y2–Y3 adoption curves); funding forces abandonment of neutrality (R3/R4 — detect at each funding gate). Each has a named mitigation and gate above; none is allowed to arrive as a surprise. + +--- + +*This roadmap is itself change-controlled. It is reviewed at every annual exit gate and revised against reality — slippage is expected and managed, not hidden. The north star does not move: **a public, neutral, multiply-implemented capability-broker standard for the agent era.*** diff --git a/WEIGHTLESS.md b/WEIGHTLESS.md new file mode 100644 index 0000000..8a306f4 --- /dev/null +++ b/WEIGHTLESS.md @@ -0,0 +1,190 @@ +# ASHE — Weightlessness + +> *Pay once at the boundary; amortize to zero across the steady state. Weight that is felt is weight in the wrong place.* + +--- + +## What "weightless" means here + +ASHE's central UX promise — stated as a mandate in [ADR-017](decisions/ADR-017-sealed-workspace-foundational-dev-pattern.md) and invoked as the "TLS for the agent layer" analogy throughout [MANIFESTO.md](MANIFESTO.md) and [VISION.md](VISION.md) — is that capability mediation must be **invisible most of the time and explicit only at risk boundaries.** This document names that promise as a single engineering property and specifies how an implementation delivers it. + +**Weightless ≠ free.** ASHE does real work: it issues leases, validates capabilities, declares intent, writes audit records, projects wire formats. Weightlessness is not the absence of that work — it is the *placement* of that work. The design principle: + +> **Move every cost to an amortizable boundary (handshake, intent declaration, lease issuance) and make the steady-state per-action path a local check with no network callout, no model token, and no human prompt.** + +This is the same move TLS makes: an expensive asymmetric handshake once per connection, then cheap symmetric operations per byte. Nobody approves every packet on an encrypted connection; the mediation happens; the user is unaware. ASHE generalizes that split from bytes to *actions*. The "weight" of mediation is paid at session establishment and amortized across the hundreds-to-thousands of actions the session subsequently performs. At `N` actions per lease, per-action overhead trends to `boundary_cost / N` → effectively zero as `N` grows. + +--- + +## The axes of weight + +"Weight" is not one thing. An honest weightlessness claim has to name every axis on which a protocol layer can be *felt* and drive each one to its floor. Each axis already has a mechanism in the ASHE corpus; this document is the place they are collected and budgeted. + +| Axis of weight | Where it would be felt | Mechanism that drives it toward zero | Anchor | +|---|---|---|---| +| **Latency** | Per-action delay on the hot path | Capability tokens validated locally per-use; network callout reserved for the slow path (sensitive ops, revocation-critical) | [ADR-007](decisions/ADR-007-interception-chain-pattern.md) (interceptor budget; token alt.) | +| **Wire / token** | Bytes and context tokens per request | Protobuf binary + HTTP/3 0-RTT + persistent multiplexed connections + tokens replacing repeated headers + TOON context projection | [ADR-012](decisions/ADR-012-wire-format-grpc-protobuf-with-projections.md), [ADR-006](decisions/ADR-006-toon-dual-projection.md), [VISION §3](VISION.md) | +| **Cognitive / friction** | Approval prompts, decisions demanded of the human | Standing capabilities + intent-declared-once + risk-tiered automation + cached approvals + inferred intent | [ADR-017](decisions/ADR-017-sealed-workspace-foundational-dev-pattern.md) (frictionlessness, mandatory) | +| **Footprint / deployment** | Binary size, memory, cold start, ops burden | Deployment profiles (ASHE-core <50 MB) + graceful degradation + intermediary-served ASHE-Lite | [ADR-009](decisions/ADR-009-deployment-profiles.md), [VISION §8](VISION.md) | +| **Model-layer** | Weights, training, context-window pollution | Non-invasive by construction — protocol operates external to the model; no RLHF, no context injection | [MANIFESTO](MANIFESTO.md) (bounded-outcomes commitment) | +| **Adoption** | Migration risk, breakage, forced rewrites | Strictly additive; ALPN multiplex on 443; HTML coexists indefinitely; never breaks the existing surface | [ADR-018](decisions/ADR-018-well-known-ashe-web-side-interaction-point.md), [VISION §4](VISION.md) | + +The thesis of this document: **weightlessness is achieved when every row's steady-state cost is paid at a boundary, not per action.** The sections below take each row in turn. + +--- + +## The four hard invariants + +Amortized-small is not zero. The strict statement of weightlessness — the bar an ASHE deployment must clear on the steady-state path — is four invariants stated as absolutes: + +| Invariant | Meaning | Status | +|---|---|---| +| **No delay** | Zero added latency on the routine action path | Target — via pre-authorization / structural boundary (below) | +| **No bandwidth** | Zero added bytes on the wire for a routine action | Target — held capability carries the grant; nothing re-sent | +| **No data alteration** | ASHE never transforms a payload or mutates application state — it is *pass/deny*, never *rewrite* | **Floor (contractual)** — [ADR-007](decisions/ADR-007-interception-chain-pattern.md) mandates idempotent, no dispatch-state mutation | +| **No interference** | ASHE does not perturb the application's normal operation or the human surface | **Floor (constructional)** — additive dual-surface ([ADR-018](decisions/ADR-018-well-known-ashe-web-side-interaction-point.md)); graceful degradation never *fails* the system ([ADR-009](decisions/ADR-009-deployment-profiles.md)) | + +Two of the four are already guaranteed, not aspired to. **No data alteration** is contractual: the interceptor contract is idempotent with no dispatch-state mutation — ASHE decides, it does not rewrite, so a passing action arrives at its handler byte-identical to an unmediated one. **No interference with existing surfaces** is constructional: ASHE rides alongside the HTML/human surface via ALPN on port 443 and never touches it, and a thin profile that lacks a tier degrades to a more conservative decision rather than erroring — it cannot take the host down. + +The other two — **no delay, no bandwidth** — are where the real engineering lives, and they expose a tension that must be named honestly. + +### The honest tension: enforcement *is* interference + +**Active enforcement is interference by definition.** The instant ASHE *blocks* an action it has interfered, and if it blocks synchronously it has also delayed. You cannot simultaneously have "veto this specific action in-band" and "zero delay / zero interference on that same action" — it is a contradiction, not an engineering gap. So literal-zero is not claimable for *every* action. It is claimable, precisely, for two categories — and weightlessness is the discipline of making those two categories cover ~98% of all actions: + +1. **Pre-authorized actions.** The capability is already held (issued at the boundary). At action time there is *nothing to check* — the decision was made before the action existed — so there is no step to add latency and no byte to send. The weight was paid at issuance and amortized away. +2. **Structurally-bounded denials.** The action was never *expressible*. (Next section.) + +The enforcement weight that genuinely remains is concentrated at the rare Tier C boundary (~2% — production deploys, secret access, escalation, irreversible destruction), where interference is the *intended function* and a few milliseconds against a deploy is invisible. Weightlessness is honest about *which* actions are truly zero and *where* the weight is deliberately spent — it does not pretend enforcement is free where enforcement is the point. + +### Structural, not procedural — the path to literal zero + +A **procedural** check is a step that *runs*: it can be made tiny, local, amortized — but never literally zero, because something executes on the path. A **structural** boundary is part of the system's *shape*: an unforgeable reference you were never handed is not a "denied check," it is an *absence*. + +This is the object-capability property at the root of ASHE's 50-year lineage ([VISION §1](VISION.md)): *ambient authority is eliminated by construction — nothing has authority it was not given.* A JPEG parser that does not hold the SSH-key capability pays **no runtime check** to be denied SSH keys — it structurally has no way to *name* them. The substrate already enforces this as its normal operation — the type system at Layer 2, the OS capability/MMU at Layer 3, the hardware root at Layer 4 ([ADR-014](decisions/ADR-014-phased-enforcement-model.md)) — so ASHE adds **no step to the path**: + +- **no delay** — there is no check to run; the boundary is the substrate's existing mechanism +- **no bandwidth** — there is no message to send; the reference is held or it is absent +- **no data alteration** — there is nothing in the path to alter +- **no interference** — the action proceeds exactly as written, or was never expressible to begin with + +The cost was paid twice, both off the hot path: once at **issuance** (a boundary event) and once at **construction** (the system shaped so authority is held-or-absent). This is why the four invariants are *reachable* rather than wishful — but only as the enforcement layer deepens. At Layer 1 (cooperating SDK) mediation is procedural and pays a small, sub-millisecond, amortized cost (§1); literal zero on delay and bandwidth is a **Layer 3/4 property**, where the boundary stops being a step and becomes part of the shape. The trajectory toward true-zero is the [ADR-014](decisions/ADR-014-phased-enforcement-model.md) trajectory. + +--- + +## Proper application — the resolving discipline + +Weightlessness is difficult to *resolve* because it is not a mechanism you install — it is **the default state of a system whose authority boundaries were applied correctly, and it is forfeited the instant they are applied incorrectly.** A misapplied ASHE is not a lighter middleware; it is just middleware. The property holds, or it doesn't, depending entirely on how it is applied. This is a binding conformance gate, not advice: [ADR-020](decisions/ADR-020-weightlessness-proper-application-conformance.md) makes "weightless" a four-part predicate an implementation may claim *only* under proper application. "Properly apply" is four facets of one discipline — all four required together; drop any one and the property collapses: + +| Facet | Proper application (weightless) | Improper application (collapses to middleware) | +|---|---|---| +| **What** — the primitive | Object-capability: authority is *held or absent*; zero ambient authority anywhere. An unauthorized action is an unnameable one. | Permission flags checked against an identity — a lookup that always runs. | +| **How** — the mechanism | Structural: the boundary is the substrate's existing mechanism (type system / OS capability / hardware root). No step added to the path. | Procedural: a check ASHE executes per action — non-zero latency and bytes, forever. | +| **When** — the moment | At construction: wall up first, develop inside ([ADR-017](decisions/ADR-017-sealed-workspace-foundational-dev-pattern.md), `ashe workspace init` as step 1, like `git init`). The system is *shaped* around the boundary. | Bolted on after the fact as a gate in front of an already-built system — always a check that runs. | +| **Where** — the scope | True-zero applied to the ~98% routine path; deliberate weight concentrated at the ~2% Tier C boundary where interference is the intended function. | The 98% checked the same way as the 2% — uniform enforcement turns every routine action into a gated one. | + +The four are not alternatives; they compose into a single test. **Apply the object-capability primitive (what), structurally (how), at construction (when), to the dominant path (where) — and the four invariants hold for free, because the substrate already does the work and the decision was made before the action existed.** Miss any one facet and weight reappears: the wrong primitive forces a lookup, procedural application forces a path step, late application forces a gate, uniform scope forces a check on every routine action. + +This is why "weightless" is hard. It is not achieved by optimizing a check until it is fast — a fast check is still a check, still non-zero, still weight. It is achieved by applying the boundary such that **there is no check at all** on the path that matters. Proper application removes the work; it does not accelerate it. + +--- + +## 1. Latency — the hot-path budget + +The interception contract in [ADR-007](decisions/ADR-007-interception-chain-pattern.md) already fixes the hard number: any pre-dispatch interceptor must meet **<5ms p99 latency, deterministic, idempotent, no dispatch-state mutation.** That is the ceiling. Weightlessness is about getting the *typical* case far below it. + +Two paths, deliberately separated: + +- **Slow path (rare, sensitive).** A full callout to the broker — fresh evaluation, current state, revocation-aware. Used for Tier C operations (~2% of actions per [ADR-017](decisions/ADR-017-sealed-workspace-foundational-dev-pattern.md)): production deploys, secret access, capability escalation, irreversible destruction. Here weight is *acceptable* because the action is rare and high-stakes; a few milliseconds of broker round-trip is invisible against a deploy. +- **Fast path (routine, ~90%+).** A locally verifiable capability token (the [ADR-007](decisions/ADR-007-interception-chain-pattern.md) alternative-3 mechanism, promoted from "v1 optimization" to the steady-state default) carries the grant. The interceptor validates the token's signature and scope *in-process* — no broker callout, no network, no I/O. This is sub-millisecond and the dominant case. + +The amortization: token issuance (the asymmetric-crypto-equivalent cost) happens once at lease establishment; token validation (the symmetric-crypto-equivalent cost) happens per action. Routine work never touches the broker. The honest tension named in [ADR-007](decisions/ADR-007-interception-chain-pattern.md) — tokens trade revocation latency for hot-path speed — is resolved by keeping revocation-critical operations on the slow path and bounding token TTL so a stale token's blast radius is time-bounded. **Weightless on the routine path; deliberately weighted on the dangerous path.** + +--- + +## 2. Wire and token weight + +At agentic-web scale the byte budget *is* an energy budget ([VISION §3](VISION.md): ~75 quintillion tokens/month at 5% agent-mediated traffic). The mechanisms compose multiplicatively, and each is a boundary cost rather than a per-action cost: + +| Mechanism | What it removes from the per-action path | Grade | +|---|---|---| +| Protobuf binary vs JSON | 5–10× payload size | Target | +| HTTP/3 + 0-RTT | TLS+TCP handshake latency on reconnect | Target | +| Persistent multiplexed connections | ~50–200 ms TLS setup *per request* | Target | +| Capability token vs cookies/headers | ~200–500 bytes of repeated metadata *per request* | Target | +| Intent-declared multi-step transactions | Collapses N round-trips to 1 | Target | +| TOON projection for agent context | Additional 40–50% context-token reduction | Target | + +The connection itself is the boundary: established once, the per-request marginal cost is a framed binary message over an already-open stream. The empirical floor for *structured-over-HTML* (2–5× token, 5× runtime) is established by [arXiv 2511.23281](https://arxiv.org/abs/2511.23281); ASHE's incremental wire optimizations above that are target-grade and gated on the [ADR-015](decisions/ADR-015-validation-methodology-and-tiered-claims.md) benchmark. No claim here is exempt from measurement. + +--- + +## 3. Cognitive weight — the friction floor + +This is the axis users actually *feel*, and the one [ADR-017](decisions/ADR-017-sealed-workspace-foundational-dev-pattern.md) makes a non-negotiable architectural commitment: **capability mediation MUST NOT mean per-operation prompts.** The mechanisms are all forms of paying the decision once and reusing it: + +- **Standing capabilities** — routine operations pre-granted at session/role level; the decision was made at lease setup, not at action time. +- **Intent declared once, actions auto-validated** — "working on the auth refactor today" is the boundary cost; every in-scope action after it is silently validated against that declaration. +- **Risk-tiered automation** — Tier A (~90%) silent, Tier B (~8%) auto-approved with audit, Tier C (~2%) explicit. Only the last tier spends human attention. +- **Cached approvals + capability inheritance through cascades** — approved once, holds for the session; sub-agents inherit attenuated capabilities without fresh approval. +- **Inferred intent** — when action patterns are unambiguous, no declaration is required at all. + +The result is the TLS feel: the developer experiences normal work with mediation running invisibly underneath, and a prompt appears only at a genuine risk boundary. The friction floor is not zero — it is *the rate of Tier C events*, which is irreducible by design (see Limits). + +--- + +## 4. Footprint, model-layer, and adoption weight + +Three axes that are weightless by *construction* rather than by amortization: + +- **Footprint.** [ADR-009](decisions/ADR-009-deployment-profiles.md) ships ASHE in profiles sharing one protocol surface; ASHE-core fits in <50 MB for embedded/edge/CI gates, and graceful degradation means a thin profile never *fails* for lack of a tier — it makes more conservative decisions. The long tail pays nothing: intermediaries (CDN-class providers) serve ASHE-Lite to hosted sites at near-zero marginal cost ([VISION §8](VISION.md)). An adopter never carries weight a richer deployment would carry. +- **Model-layer.** ASHE is non-invasive by definition: no weight changes, no training-time constraints, no context-window pollution, no RLHF cooperation required. The model carries zero ASHE weight because the protocol lives entirely external to it at the dispatch/lease/audit boundary. This is the load-bearing **bounded-outcomes ≠ censored-behavior** property — the model's cognition is untouched. +- **Adoption.** Strictly additive ([ADR-018](decisions/ADR-018-well-known-ashe-web-side-interaction-point.md)): `.well-known/ashe` sits alongside existing HTML, ALPN multiplexes both protocols on port 443, and the human surface is never disturbed. Adoption weight is near-zero because there is nothing to migrate and nothing that can break. + +--- + +## Evidence grades + +Per [ADR-015](decisions/ADR-015-validation-methodology-and-tiered-claims.md), weightlessness claims carry explicit grades: + +| Claim | Grade | Basis | +|---|---|---| +| Hot-path interceptor overhead is bounded at <5 ms p99 | **Floor (design-contractual)** | [ADR-007](decisions/ADR-007-interception-chain-pattern.md) interceptor obligation; conformance-checkable | +| Structured alternatives beat HTML by 2–5× token / 5× runtime | **Floor (empirical)** | [arXiv 2511.23281](https://arxiv.org/abs/2511.23281) controlled study | +| Local token validation drives typical hot-path overhead sub-millisecond | **Target** | Mechanism named ([ADR-007](decisions/ADR-007-interception-chain-pattern.md) alt. 3); subject to benchmark | +| ASHE's incremental wire optimizations reach 10–30× vs HTML | **Target** | Mechanism-by-mechanism; gated on [ADR-015](decisions/ADR-015-validation-methodology-and-tiered-claims.md) benchmark | +| ~90% of actions traverse the silent fast path | **Target** | Risk-tier distribution per [ADR-017](decisions/ADR-017-sealed-workspace-foundational-dev-pattern.md); deployment-dependent | +| Cascade + persistent-session patterns compound to 20–50× per-task | **Stretch** | Favorable-condition economics ([VISION §7](VISION.md)) | + +--- + +## Honest limits — where weight is irreducible + +Weightlessness is bounded protection, not a free lunch. The places weight cannot go to zero, named plainly: + +- **The Tier C boundary is the friction floor.** Production deploys, secret access, capability escalation, and irreversible destruction *should* cost a deliberate human decision. Driving this to zero would defeat the purpose. Weightlessness means making this rare (~2%), not absent. +- **Cold start is a real boundary cost.** The first action in a session pays the handshake — lease establishment, intent declaration, connection setup. Amortization works only when `N` is large; a one-shot, single-action agent sees the boundary cost undiluted. ASHE is weightless *over a session*, not *over a single isolated call*. +- **Revocation trades against token speed.** The fast path's locally-validated tokens are exactly what makes near-instant revocation hard. The resolution (slow-path for revocation-critical ops + bounded TTL) caps the staleness window; it does not eliminate the tension. This is an honest [ADR-007](decisions/ADR-007-interception-chain-pattern.md) trade-off, not a solved problem. +- **Audit is weight that must not be amortized away.** Every capability exercise is recorded ([VISION §4](VISION.md)). Audit write cost is real and is deliberately *not* dropped on the fast path — it is made cheap (append-only, async, off the critical path) but never skipped. Forensic reconstructability is load-bearing; its cost is accepted. + +--- + +## What weightlessness asks of an implementation + +A conformant implementation claiming weightlessness commits to a budget, checkable against the conformance suite ([ADR-015](decisions/ADR-015-validation-methodology-and-tiered-claims.md)): + +| Property | Budget | Verified by | +|---|---|---| +| Routine fast-path action | No broker callout; in-process token validation | Trace shows zero network I/O on Tier A path | +| Interceptor p99 | <5 ms ([ADR-007](decisions/ADR-007-interception-chain-pattern.md)) | Load test under representative mix | +| Human prompts | Only on Tier C; standing capability holds otherwise | Prompt count ≈ Tier C event count | +| Per-request wire overhead | Framed binary on a persistent stream; no per-request handshake | Wire capture | +| Embedded footprint | ASHE-core <50 MB ([ADR-009](decisions/ADR-009-deployment-profiles.md)) | Binary size measurement | +| Model footprint | Zero weight/context changes | Construction (external to model) | + +**The one-line test of weightlessness:** in steady state, a routine agent action should cost no network round-trip, no model token, and no human attention — and the audit record should still exist. If any of those three are spent on a Tier A action, weight has leaked into the wrong place. + +--- + +*ASHE — weightlessness is the placement of cost, not its absence. Pay at the boundary; run free in the steady state.* +*Companion to [MANIFESTO.md](MANIFESTO.md), [VISION.md](VISION.md). Evidence-graded per [ADR-015](decisions/ADR-015-validation-methodology-and-tiered-claims.md). Honest limits named.* diff --git a/conformance/README.md b/conformance/README.md new file mode 100644 index 0000000..4f555c9 --- /dev/null +++ b/conformance/README.md @@ -0,0 +1,108 @@ +# ASHE Conformance Suite + +The spec's runnable arm. Where the prose says an implementation "MUST," this is where +that requirement becomes a test an implementation can pass or fail. + +**v0.1 ships one gate: the weightlessness gate of [ADR-020](../decisions/ADR-020-weightlessness-proper-application-conformance.md).** +A weightlessness claim is conformant only under *proper application* — the +object-capability primitive (**what**), applied structurally (**how**), at +construction (**when**), on the dominant path (**where**). The suite turns those four +facets into four conjunctive test groups; all four must pass. + +## Layout + +``` +conformance/ + src/ + adapter.ts # the contract a SUT implements (the four facets) + manifest.ts # language-neutral test definitions (W/H/N/R) + setup.ts # loads the adapter named by $ASHE_CONFORMANCE_ADAPTER + protocol/ # nascent reference implementation (see below) + capability.ts # unforgeable object-capabilities + attenuable sets + actor.ts # principals; structural cascade attenuation + lease.ts # boundary-amortized standing authority + tier.ts # risk-tier classification (A/B routine, C boundary) + mediation.ts # structural interception point (ADR-007) + audit.ts # tamper-evident audit-by-construction (hash chain) + intent.ts # declare-once intent reconciliation (VISION §6) + examples/structural-reference-adapter.ts # correctly-applied SUT, built on protocol/ + tests/ + protocol/*.test.ts # unit tests for the primitives (always run) + group-w-what.test.ts # object-capability primitive + group-h-how.test.ts # structural mechanism + group-n-when.test.ts # at construction + group-r-where.test.ts # concentrated scope +``` + +## `src/protocol/` — the nascent reference implementation + +The example adapter is no longer a toy: it delegates to real protocol primitives in +`src/protocol/`, the embryonic in-memory reference implementation of ASHE's +object-capability core. These are the genuine primitives, exercised both by their own +unit tests and (via the adapter) by the weightlessness gate: + +- **`capability.ts`** — capabilities are *unforgeable* (the mint token is module-private; + `new Capability(...)` from outside throws). `CapabilitySet.attenuate()` can only drop + authority — there is no `grant`/`union`, so amplification is *unconstructable*, not merely checked. +- **`actor.ts`** — a principal holds a set and nothing ambient; `spawn()` attenuates, so a + sub-actor exceeding its parent cannot be built (cascade attenuation, ADR-017). +- **`lease.ts`** — authority is issued at a boundary with a TTL; the cost is paid once, then + the steady-state path is free (WEIGHTLESS amortization). +- **`tier.ts`** — routine (A/B) vs the deliberate-weight Tier-C boundary. +- **`mediation.ts`** — the interception point (ADR-007), structural: routine held actions pass + through with no boundary step and byte-identical payload; an unheld capability is `UNNAMEABLE`, + never `DENIED`. Optionally emits an audit record per decision. +- **`audit.ts`** — append-only, SHA-256 hash-chained audit log (ADR-013 Audit service; ADR-016 + provenance). `verify()` detects any reorder/edit/drop of a sealed record; the local append sits + off the action's critical path (not a round-trip). +- **`intent.ts`** — declare-once intent reconciliation (VISION §6; ADR-017 C2). An in-scope, + unexpired action reconciles silently (no prompt); out-of-scope or expired escalates. + +## The four groups (ADR-020) + +| Group | Facet | Tests | The disqualifying case | +|---|---|---|---| +| **W** | *what* | unnameability, zero-ambient-authority, attenuation | the SUT returns a **DENIED** decision for an unauthorized action (it was nameable and evaluated) | +| **H** | *how* | no-added-step, byte-identity, layer-disclosure | a **literal-zero** claim sits on top of a procedural routine-path check | +| **N** | *when* | construction-order, no-front-gate | disabling ASHE leaves a guarded action **reachable** (it was a removable front gate) | +| **R** | *where* | path-classification, no-uniform-enforcement, friction-frequency | a routine action incurs a round-trip / token / prompt (the 98% gated like the 2%) | + +Results are **graded, not binary**: Group H records whether no-delay/no-bandwidth hold +as `literal-zero` (structural, Layer 3/4) or `amortized-small` (procedural, Layer 1, +disclosed) per [ADR-015](../decisions/ADR-015-validation-methodology-and-tiered-claims.md). +A Layer-1 implementation passes honestly; it does not pass by claiming literal-zero. + +## Running + +```bash +cd conformance +npm install + +# Against the illustrative reference adapter — self-verifies the suite is green: +npm run test:example + +# Against your implementation: point the env var at a module whose default export +# is an AsheConformanceAdapter (see src/adapter.ts), then: +ASHE_CONFORMANCE_ADAPTER=./path/to/your-adapter.ts npm test + +# With no adapter configured, the protocol unit tests still run, and every +# conformance group SKIPS (the suite makes no claim about a SUT that has not been +# wired in): +npm test +``` + +## Plugging in an implementation + +Implement `AsheConformanceAdapter` (`src/adapter.ts`) over your implementation. The +adapter is the only ASHE-specific surface the suite touches; it is the seam between +the language-neutral manifest and a concrete implementation. The four method blocks +map one-to-one to the four facets. `src/examples/structural-reference-adapter.ts` is a +minimal, fully-passing reference — read it as the shape, not as an enforcement engine. + +## Status + +v0.1, scaffold. The weightlessness gate is the first gate implemented; the +conformance-suite commitments in ADR-001 (tiered conformance), ADR-015 (validation +methodology), and ADR-017 (sealed-workspace suite) are the roadmap for subsequent +gates. The manifest is versioned with the spec so results are comparable across +implementations and across the four ADR-014 enforcement layers. diff --git a/conformance/package-lock.json b/conformance/package-lock.json new file mode 100644 index 0000000..3c4067a --- /dev/null +++ b/conformance/package-lock.json @@ -0,0 +1,1462 @@ +{ + "name": "@ashe/conformance", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@ashe/conformance", + "version": "0.1.0", + "license": "Apache-2.0", + "devDependencies": { + "@types/node": "^20.0.0", + "typescript": "^5.5.0", + "vitest": "^2.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz", + "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz", + "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz", + "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz", + "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz", + "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz", + "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz", + "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz", + "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz", + "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz", + "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz", + "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz", + "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz", + "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz", + "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz", + "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz", + "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz", + "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz", + "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz", + "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz", + "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz", + "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz", + "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz", + "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz", + "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz", + "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vitest/expect": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", + "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", + "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", + "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.9", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", + "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", + "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", + "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz", + "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.4", + "@rollup/rollup-android-arm64": "4.60.4", + "@rollup/rollup-darwin-arm64": "4.60.4", + "@rollup/rollup-darwin-x64": "4.60.4", + "@rollup/rollup-freebsd-arm64": "4.60.4", + "@rollup/rollup-freebsd-x64": "4.60.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", + "@rollup/rollup-linux-arm-musleabihf": "4.60.4", + "@rollup/rollup-linux-arm64-gnu": "4.60.4", + "@rollup/rollup-linux-arm64-musl": "4.60.4", + "@rollup/rollup-linux-loong64-gnu": "4.60.4", + "@rollup/rollup-linux-loong64-musl": "4.60.4", + "@rollup/rollup-linux-ppc64-gnu": "4.60.4", + "@rollup/rollup-linux-ppc64-musl": "4.60.4", + "@rollup/rollup-linux-riscv64-gnu": "4.60.4", + "@rollup/rollup-linux-riscv64-musl": "4.60.4", + "@rollup/rollup-linux-s390x-gnu": "4.60.4", + "@rollup/rollup-linux-x64-gnu": "4.60.4", + "@rollup/rollup-linux-x64-musl": "4.60.4", + "@rollup/rollup-openbsd-x64": "4.60.4", + "@rollup/rollup-openharmony-arm64": "4.60.4", + "@rollup/rollup-win32-arm64-msvc": "4.60.4", + "@rollup/rollup-win32-ia32-msvc": "4.60.4", + "@rollup/rollup-win32-x64-gnu": "4.60.4", + "@rollup/rollup-win32-x64-msvc": "4.60.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup/node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", + "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", + "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.9", + "@vitest/mocker": "2.1.9", + "@vitest/pretty-format": "^2.1.9", + "@vitest/runner": "2.1.9", + "@vitest/snapshot": "2.1.9", + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.9", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.9", + "@vitest/ui": "2.1.9", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/conformance/package.json b/conformance/package.json new file mode 100644 index 0000000..68733b5 --- /dev/null +++ b/conformance/package.json @@ -0,0 +1,19 @@ +{ + "name": "@ashe/conformance", + "version": "0.1.0", + "private": true, + "type": "module", + "description": "Executable conformance suite for the ASHE specification. v0.1 ships the weightlessness gate (ADR-020). Implementations plug in via an adapter; the suite is the spec's runnable arm.", + "license": "Apache-2.0", + "scripts": { + "test": "vitest run", + "test:watch": "vitest", + "test:example": "ASHE_CONFORMANCE_ADAPTER=./src/examples/structural-reference-adapter.ts vitest run", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "typescript": "^5.5.0", + "vitest": "^2.0.0" + } +} diff --git a/conformance/src/adapter.ts b/conformance/src/adapter.ts new file mode 100644 index 0000000..94e1b6a --- /dev/null +++ b/conformance/src/adapter.ts @@ -0,0 +1,114 @@ +/** + * ASHE conformance adapter — the contract an implementation-under-test (the "SUT") + * implements so the weightlessness-gate suite (ADR-020) can exercise it. + * + * The suite never imports an implementation directly. An implementation provides a + * module whose default export is an `AsheConformanceAdapter`, points + * `ASHE_CONFORMANCE_ADAPTER` at it, and runs the suite. With no adapter configured, + * every group is skipped (see `requireAdapter`). + * + * The four method blocks below map one-to-one to the four facets of proper + * application in ADR-020: what / how / when / where. + */ + +/** Enforcement layer per ADR-014: 1 cooperating-SDK, 2 runtime-hook, 3 OS, 4 hardware. */ +export type EnforcementLayer = 1 | 2 | 3 | 4; + +/** Risk tier per ADR-017: A routine (~90%), B medium (~8%), C high-stakes (~2%). */ +export type Tier = "A" | "B" | "C"; + +/** Disclosed grade for an invariant on the routine path (ADR-015 / ADR-020 caveat). */ +export type Grade = "literal-zero" | "amortized-small"; + +/** Outcome of attempting an action. The W/H distinction is load-bearing: an + * unauthorized action under proper application is UNNAMEABLE (no reference to + * invoke), never DENIED (a flag evaluated at action time). */ +export type InvokeResult = "ALLOWED" | "UNNAMEABLE" | "DENIED"; + +/** Opaque handle to an actor constructed by the SUT. */ +export type Actor = unknown; + +/** A capability the SUT understands, named however the SUT names them. */ +export type CapabilityRef = string; + +/** Describes an action the suite asks the SUT to attempt or classify. */ +export interface ActionDescriptor { + /** Stable id for logging. */ + readonly id: string; + /** The capability this action requires to be nameable. */ + readonly requiredCapability: CapabilityRef; + /** The risk tier the action is expected to fall in (for the where-facet tests). */ + readonly expectedTier: Tier; + /** Opaque application payload, used by the byte-identity test. */ + readonly payload: Uint8Array; +} + +export interface AsheConformanceAdapter { + // ── Facet WHAT — object-capability primitive ────────────────────────────── + /** Construct a fresh actor holding exactly `grants` and nothing ambient. */ + makeActor(grants: CapabilityRef[]): Actor; + /** Every authority reachable by `actor`, including anything recoverable via + * traversal / absolute paths / subprocess / reflection. Zero-ambient means + * this equals exactly the granted set. */ + reachableAuthorities(actor: Actor): CapabilityRef[]; + /** Does `actor` hold a *reference* by which `action` can be invoked at all? */ + canName(actor: Actor, action: ActionDescriptor): boolean; + /** Attempt `action`. Must distinguish UNNAMEABLE (no reference) from DENIED + * (reference present, evaluated, rejected) from ALLOWED. */ + invoke(actor: Actor, action: ActionDescriptor): InvokeResult; + /** Spawn a sub-actor with `grants`; used for the cascade-attenuation test. */ + spawnSubActor(parent: Actor, grants: CapabilityRef[]): Actor; + + // ── Facet HOW — structural mechanism ────────────────────────────────────── + /** The enforcement layer the SUT applies on the routine (Tier A/B) path. */ + routinePathLayer(): EnforcementLayer; + /** Run a passing routine action; report whether an ASHE-executed evaluation + * step was interposed on the path, and the payload as delivered to the handler. */ + routineActionTrace(action: ActionDescriptor): { + stepInterposed: boolean; + payloadDelivered: Uint8Array; + }; + /** The grade the SUT *claims* for an invariant on the routine path. A Layer-1 + * SUT must declare "amortized-small" for no-delay/no-bandwidth, not literal-zero. */ + declaredGrade(invariant: "no-delay" | "no-bandwidth"): Grade; + + // ── Facet WHEN — at construction ────────────────────────────────────────── + /** True iff the perimeter is established before workspace contents are + * reachable (wall-up-first, `ashe workspace init` as step 1; ADR-017 C1). */ + perimeterEstablishedAtConstruction(): boolean; + /** With the ASHE component disabled, is `action` UNREACHABLE (structural shape) + * or REACHABLE (a removable front gate — bolted on)? */ + guardedReachabilityWithAsheDisabled(action: ActionDescriptor): + | "unreachable" + | "reachable"; + + // ── Facet WHERE — concentrated scope ────────────────────────────────────── + /** Which tier the SUT assigns `action`. */ + classifyTier(action: ActionDescriptor): Tier; + /** Cost the SUT incurs for `action` on its path. */ + costProfile(action: ActionDescriptor): { + roundTrip: boolean; + addedTokens: number; + humanPrompt: boolean; + }; + /** Fraction (0..1) of routine-path actions in `workload` that triggered an + * explicit human prompt. */ + routinePromptFrequency(workload: ActionDescriptor[]): number; + /** The SUT's own declared frictionlessness threshold (ADR-017 C2). */ + promptFrequencyThreshold(): number; + + // ── Shared baseline ─────────────────────────────────────────────────────── + /** The payload an *unmediated* invocation of `action` would deliver to the + * handler — the byte-identity baseline for the no-data-alteration floor. */ + unmediatedBaselinePayload(action: ActionDescriptor): Uint8Array; +} + +/** Returns the configured adapter, or null when none is registered. */ +export function loadAdapter(): AsheConformanceAdapter | null { + return (globalThis as { __ASHE_ADAPTER__?: AsheConformanceAdapter }).__ASHE_ADAPTER__ ?? null; +} + +/** Resolve the adapter for a test group, or null to signal the group should skip. */ +export function requireAdapter(): AsheConformanceAdapter | null { + return loadAdapter(); +} diff --git a/conformance/src/examples/structural-reference-adapter.ts b/conformance/src/examples/structural-reference-adapter.ts new file mode 100644 index 0000000..6f6047c --- /dev/null +++ b/conformance/src/examples/structural-reference-adapter.ts @@ -0,0 +1,136 @@ +/** + * Reference adapter — a *correctly applied* ASHE (object-capability primitive, + * structural at Layer 3, established at construction, weight concentrated at Tier C), + * built on the reference protocol primitives in `../protocol`. It makes the + * conformance suite self-verifying (`npm run test:example` → all groups green) and + * shows implementers the shape of a conformant adapter: a thin translation from the + * suite's `AsheConformanceAdapter` contract to real protocol objects. + * + * It is in-memory and minimal — not a production enforcement engine — but the + * authority model it exposes (unforgeable capabilities, structural attenuation, + * routine-path pass-through) is the genuine primitive set, not a stub. + */ +import type { + ActionDescriptor, + Actor as OpaqueActor, + AsheConformanceAdapter, + CapabilityRef, + EnforcementLayer, + Grade, + InvokeResult, + Tier, +} from "../adapter.js"; +import { + Actor, + CapabilityIssuer, + CapabilitySet, + DEFAULT_TIER_C, + Mediator, + TierRegistry, +} from "../protocol/index.js"; + +const issuer = new CapabilityIssuer(); +const tiers = new TierRegistry().registerAll(DEFAULT_TIER_C, "C"); +const mediator = new Mediator(tiers); + +/** Mint a fresh capability per requested name (the issuer is the trust root). */ +function setFor(grants: CapabilityRef[]): CapabilitySet { + return new CapabilitySet(grants.map((name) => issuer.mint(name))); +} + +function asActor(a: OpaqueActor): Actor { + if (!(a instanceof Actor)) throw new Error("adapter received a foreign actor handle"); + return a; +} + +let actorSeq = 0; + +const adapter: AsheConformanceAdapter = { + // ── what ────────────────────────────────────────────────────────────────── + makeActor(grants: CapabilityRef[]): OpaqueActor { + actorSeq += 1; + return new Actor(`actor#${actorSeq}`, setFor(grants)); + }, + + reachableAuthorities(actor: OpaqueActor): CapabilityRef[] { + return asActor(actor).reachableAuthorities(); + }, + + canName(actor: OpaqueActor, action: ActionDescriptor): boolean { + return asActor(actor).canName(action.requiredCapability); + }, + + invoke(actor: OpaqueActor, action: ActionDescriptor): InvokeResult { + return mediator.mediate(asActor(actor), { + capability: action.requiredCapability, + payload: action.payload, + }).decision; + }, + + spawnSubActor(parent: OpaqueActor, grants: CapabilityRef[]): OpaqueActor { + actorSeq += 1; + // Attenuation is structural: the child can only keep names the parent holds. + return asActor(parent).spawn(`actor#${actorSeq}`, grants); + }, + + // ── how ─────────────────────────────────────────────────────────────────── + routinePathLayer(): EnforcementLayer { + return 3; // OS-level structural mediation: the substrate is the boundary. + }, + + routineActionTrace(action: ActionDescriptor) { + // A held routine capability: the mediator interposes no Tier-C boundary, and the + // payload is delivered byte-identical. + const probe = new Actor("trace-probe", setFor([action.requiredCapability])); + const r = mediator.mediate(probe, { + capability: action.requiredCapability, + payload: action.payload, + }); + return { stepInterposed: r.boundaryInvoked, payloadDelivered: r.payloadDelivered }; + }, + + declaredGrade(_invariant: "no-delay" | "no-bandwidth"): Grade { + return "literal-zero"; // honest at Layer 3 + }, + + // ── when ────────────────────────────────────────────────────────────────── + perimeterEstablishedAtConstruction(): boolean { + return true; // wall-up-first + }, + + guardedReachabilityWithAsheDisabled(_action: ActionDescriptor) { + // The boundary is the system's shape: there is no "ASHE off" mode that restores + // ambient access, because the capability references do not exist outside it. + return "unreachable" as const; + }, + + // ── where ───────────────────────────────────────────────────────────────── + classifyTier(action: ActionDescriptor): Tier { + return tiers.classify(action.requiredCapability); + }, + + costProfile(action: ActionDescriptor) { + if (this.classifyTier(action) === "C") { + // Deliberate weight at the ~2% boundary — interference is the intended function. + return { roundTrip: true, addedTokens: 0, humanPrompt: true }; + } + // The ~98% routine path: no round-trip, no token, no prompt. + return { roundTrip: false, addedTokens: 0, humanPrompt: false }; + }, + + routinePromptFrequency(workload: ActionDescriptor[]): number { + const prompted = workload.filter((a) => this.costProfile(a).humanPrompt).length; + return workload.length === 0 ? 0 : prompted / workload.length; + }, + + promptFrequencyThreshold(): number { + return 0.05; + }, + + // ── shared ────────────────────────────────────────────────────────────────── + unmediatedBaselinePayload(action: ActionDescriptor): Uint8Array { + return action.payload; + }, +}; + +export default adapter; diff --git a/conformance/src/manifest.ts b/conformance/src/manifest.ts new file mode 100644 index 0000000..425c9f1 --- /dev/null +++ b/conformance/src/manifest.ts @@ -0,0 +1,139 @@ +/** + * Language-neutral manifest of the weightlessness-gate conformance tests (ADR-020, + * "Conformance suite for the weightlessness gate"). The four groups W/H/N/R map to + * the four facets of proper application. All four groups are CONJUNCTIVE: a + * weightlessness claim is conformant only if every group passes. + * + * Non-TypeScript implementations consume this same manifest (by id) so results are + * comparable across implementations and across the four ADR-014 enforcement layers. + */ + +export type Facet = "what" | "how" | "when" | "where"; + +export interface ConformanceTest { + /** Stable id, cited in results and in cross-implementation comparison. */ + readonly id: string; + readonly group: "W" | "H" | "N" | "R"; + readonly facet: Facet; + readonly name: string; + /** The normative requirement, paraphrased from ADR-020. */ + readonly requirement: string; + /** The condition under which the test fails (the disqualifying case). */ + readonly failsIf: string; +} + +export const MANIFEST: readonly ConformanceTest[] = [ + // ── Group W — what (object-capability primitive) ────────────────────────── + { + id: "W1-unnameability", + group: "W", + facet: "what", + name: "unnameability", + requirement: + "An actor lacking a capability MUST have no reference by which to invoke the guarded action; the action is absent from the actor's surface, not present-and-rejected.", + failsIf: + "The SUT returns a DENIED decision for an unauthorized action (denial implies the action was nameable and evaluated).", + }, + { + id: "W2-zero-ambient-authority", + group: "W", + facet: "what", + name: "zero-ambient-authority", + requirement: + "Authority reachable by a freshly-constructed actor MUST equal exactly the explicitly-granted set, with no ambient authority recoverable via traversal, absolute paths, subprocess, or reflection.", + failsIf: "Reachable authority is a strict superset of the granted set.", + }, + { + id: "W3-attenuation", + group: "W", + facet: "what", + name: "cascade-attenuation", + requirement: + "A sub-actor's reachable authority MUST be a subset of the granting actor's (ADR-017 cascade attenuation).", + failsIf: "A spawned sub-actor can reach an authority the parent cannot.", + }, + + // ── Group H — how (structural mechanism) ────────────────────────────────── + { + id: "H1-no-added-step", + group: "H", + facet: "how", + name: "no-added-step", + requirement: + "On the routine path, a passing action MUST reach its handler with no ASHE-executed evaluation step interposed at the claimed structural layer (substrate's own mechanism per ADR-014).", + failsIf: + "An ASHE evaluation step is interposed on the routine path while the SUT claims a structural (Layer 2/3/4) boundary.", + }, + { + id: "H2-byte-identity", + group: "H", + facet: "how", + name: "byte-identity", + requirement: + "A passing routine action MUST arrive at its handler byte-identical to the unmediated baseline (ADR-007 no-data-alteration floor).", + failsIf: "The delivered payload differs from the unmediated baseline.", + }, + { + id: "H3-layer-disclosure", + group: "H", + facet: "how", + name: "layer-disclosure", + requirement: + "If routine-path mediation is procedural (Layer 1), the SUT MUST declare no-delay/no-bandwidth as amortized-small, not literal-zero.", + failsIf: + "The SUT runs a procedural routine-path check (Layer 1) yet declares literal-zero for no-delay or no-bandwidth.", + }, + + // ── Group N — when (at construction) ────────────────────────────────────── + { + id: "N1-construction-order", + group: "N", + facet: "when", + name: "construction-order", + requirement: + "The perimeter MUST be established before workspace contents are reachable (wall-up-first; `ashe workspace init` as step 1; ADR-017 C1).", + failsIf: "There is a window in which contents are reachable before the perimeter exists.", + }, + { + id: "N2-no-front-gate", + group: "N", + facet: "when", + name: "no-front-gate", + requirement: + "The boundary MUST be the system's shape, not a removable gate: disabling the ASHE component MUST make guarded actions unreachable (structural), not ungated-but-reachable (bolted-on).", + failsIf: "Disabling ASHE leaves a guarded action reachable.", + }, + + // ── Group R — where (concentrated scope) ────────────────────────────────── + { + id: "R1-path-classification", + group: "R", + facet: "where", + name: "path-classification", + requirement: + "The SUT MUST classify actions into the routine path (Tier A/B) and the Tier C boundary, and enforce them differently.", + failsIf: "The SUT does not distinguish the routine path from the Tier C boundary.", + }, + { + id: "R2-no-uniform-enforcement", + group: "R", + facet: "where", + name: "no-uniform-enforcement", + requirement: + "A routine Tier A action MUST incur no round-trip, no added token, and no human prompt, while a representative Tier C action does invoke the explicit boundary.", + failsIf: "A routine action incurs round-trip/added-tokens/human-prompt, i.e. the 98% is gated like the 2%.", + }, + { + id: "R3-friction-frequency", + group: "R", + facet: "where", + name: "friction-frequency", + requirement: + "Over a representative workload, explicit-prompt frequency on the routine path MUST stay below the SUT's declared threshold (ADR-017 C2 frictionlessness).", + failsIf: "Routine-path prompt frequency meets or exceeds the declared threshold.", + }, +] as const; + +export function testsForGroup(group: ConformanceTest["group"]): readonly ConformanceTest[] { + return MANIFEST.filter((t) => t.group === group); +} diff --git a/conformance/src/protocol/actor.ts b/conformance/src/protocol/actor.ts new file mode 100644 index 0000000..8abfbae --- /dev/null +++ b/conformance/src/protocol/actor.ts @@ -0,0 +1,31 @@ +/** + * An actor is a principal that holds a {@link CapabilitySet} and nothing ambient. + * Its entire authority is the set it holds; `spawn` can only hand a child a subset + * (cascade attenuation, ADR-017), because {@link CapabilitySet.attenuate} cannot + * amplify. A sub-actor exceeding its parent is therefore not "rejected" — it is + * unconstructable. + */ +import { CapabilitySet } from "./capability.js"; + +export class Actor { + constructor( + public readonly id: string, + public readonly capabilities: CapabilitySet, + ) {} + + /** Does this actor hold a reference by which `capabilityName` can be named? */ + canName(capabilityName: string): boolean { + return this.capabilities.has(capabilityName); + } + + /** Every authority reachable by this actor — exactly the held set, nothing more. + * There is no ambient surface from which to recover anything else. */ + reachableAuthorities(): string[] { + return this.capabilities.names(); + } + + /** Spawn a sub-actor holding at most the named subset of this actor's authority. */ + spawn(childId: string, allowedNames: Iterable): Actor { + return new Actor(childId, this.capabilities.attenuate(allowedNames)); + } +} diff --git a/conformance/src/protocol/audit.ts b/conformance/src/protocol/audit.ts new file mode 100644 index 0000000..e4ffece --- /dev/null +++ b/conformance/src/protocol/audit.ts @@ -0,0 +1,85 @@ +/** + * Audit-by-construction (ADR-013 Audit service; ADR-016 provenance). Every mediation + * decision emits an immutable record into an append-only, hash-chained log, so the + * trail is tamper-evident by construction rather than reconstructed after the fact. + * + * This is a *local* append (the audit subsystem of WEIGHTLESS.md), not a network + * callout: it does not sit on the action's critical path as a round-trip, and it + * never mutates the payload. The hash chain gives provenance — any reordering or + * edit of a past record breaks `verify()`. + */ +import { createHash } from "node:crypto"; +import type { Decision } from "./mediation.js"; +import type { Tier } from "./tier.js"; + +export interface AuditEntry { + readonly subject: string; + readonly capability: string; + readonly tier: Tier; + readonly decision: Decision; + readonly boundaryInvoked: boolean; + readonly at: number; +} + +export interface AuditRecord extends AuditEntry { + readonly seq: number; + /** Hash of the previous record (or the genesis constant for the first). */ + readonly prevHash: string; + /** Hash over (prevHash + canonical entry) — the chain link. */ + readonly hash: string; +} + +const GENESIS = "0".repeat(64); + +function hashRecord(prevHash: string, seq: number, e: AuditEntry): string { + const canonical = JSON.stringify([ + prevHash, + seq, + e.subject, + e.capability, + e.tier, + e.decision, + e.boundaryInvoked, + e.at, + ]); + return createHash("sha256").update(canonical).digest("hex"); +} + +export class AuditLog { + private readonly chain: AuditRecord[] = []; + + /** Append an entry, linking it into the hash chain. Returns the sealed record. */ + append(entry: AuditEntry): AuditRecord { + const seq = this.chain.length; + const prevHash = seq === 0 ? GENESIS : this.chain[seq - 1]!.hash; + const hash = hashRecord(prevHash, seq, entry); + const record: AuditRecord = { ...entry, seq, prevHash, hash }; + this.chain.push(record); + return record; + } + + entries(): readonly AuditRecord[] { + return this.chain; + } + + get length(): number { + return this.chain.length; + } + + /** The head hash — commits to the entire history. */ + get head(): string { + return this.chain.length === 0 ? GENESIS : this.chain[this.chain.length - 1]!.hash; + } + + /** Recompute the chain; false if any record was reordered, edited, or dropped. */ + verify(): boolean { + let prevHash = GENESIS; + for (let i = 0; i < this.chain.length; i++) { + const r = this.chain[i]!; + if (r.seq !== i || r.prevHash !== prevHash) return false; + if (hashRecord(prevHash, i, r) !== r.hash) return false; + prevHash = r.hash; + } + return true; + } +} diff --git a/conformance/src/protocol/capability.ts b/conformance/src/protocol/capability.ts new file mode 100644 index 0000000..7bda99b --- /dev/null +++ b/conformance/src/protocol/capability.ts @@ -0,0 +1,94 @@ +/** + * Object-capability primitive (VISION §1; ADR-003 invariant language). + * + * A capability is an *unforgeable* reference. Authority is the reference itself: + * you hold it or you do not. There is no ambient table to consult, and there is no + * way to fabricate a capability from outside this module — the only path to one is + * being handed it by a `CapabilityIssuer`. This is what makes an unauthorized action + * *unnameable* rather than *denied*: an actor without the reference has nothing to + * invoke, so there is no check to run (WEIGHTLESS.md, "structural, not procedural"). + */ + +/** Module-private mint token. `new Capability(...)` from outside this module throws, + * so capabilities cannot be forged — only issued. */ +const MINT: unique symbol = Symbol("ashe.capability.mint"); + +export class Capability { + /** @internal — use {@link CapabilityIssuer.mint}. */ + constructor( + token: symbol, + public readonly name: string, + public readonly id: string, + ) { + if (token !== MINT) { + throw new Error( + "capabilities are unforgeable: obtain one from a CapabilityIssuer, do not construct it", + ); + } + } +} + +/** The sole legitimate source of capabilities. A deployment holds an issuer at the + * trust root; everything downstream receives references, never the mint. */ +export class CapabilityIssuer { + private seq = 0; + mint(name: string): Capability { + this.seq += 1; + return new Capability(MINT, name, `${name}#${this.seq}`); + } +} + +/** + * An immutable bundle of held capabilities. The only derivation operation is + * {@link attenuate} — you can drop authority, never add it. There is deliberately + * no `union`/`grant` method: amplification is not expressible, which is the + * cascade-attenuation invariant (ADR-017) made structural rather than checked. + */ +export class CapabilitySet { + private readonly byName: ReadonlyMap; + + constructor(caps: Iterable = []) { + const m = new Map(); + for (const c of caps) m.set(c.name, c); + this.byName = m; + } + + /** True iff a capability with this name is held. */ + has(name: string): boolean { + return this.byName.has(name); + } + + /** True iff this exact capability reference is held (identity, not name). */ + holds(cap: Capability): boolean { + return this.byName.get(cap.name) === cap; + } + + names(): string[] { + return [...this.byName.keys()]; + } + + get size(): number { + return this.byName.size; + } + + /** Derive a strictly-not-larger set keeping only the named capabilities that are + * already held. Names not held are silently dropped — you cannot attenuate + * *upward* into authority you never had. */ + attenuate(allowedNames: Iterable): CapabilitySet { + const allow = new Set(allowedNames); + const kept: Capability[] = []; + for (const [name, cap] of this.byName) { + if (allow.has(name)) kept.push(cap); + } + return new CapabilitySet(kept); + } + + /** True iff every capability in `other` is held here (identity-wise). */ + contains(other: CapabilitySet): boolean { + for (const name of other.names()) { + const here = this.byName.get(name); + if (!here || !other.holds(here)) return false; + } + return true; + } +} diff --git a/conformance/src/protocol/index.ts b/conformance/src/protocol/index.ts new file mode 100644 index 0000000..96e8b79 --- /dev/null +++ b/conformance/src/protocol/index.ts @@ -0,0 +1,23 @@ +/** + * ASHE reference protocol primitives — the embryonic reference implementation the + * conformance suite's example adapter wraps. Object-capability core (capability), + * principals (actor), boundary-amortized authority (lease), risk-tier scope (tier), + * the structural interception point (mediation), tamper-evident audit-by-construction + * (audit), and declare-once intent reconciliation (intent). + * + * This is intentionally small and in-memory: enough to make the weightlessness gate + * (ADR-020) pass against a *correctly applied* implementation, and to grow into. + */ +export { Capability, CapabilityIssuer, CapabilitySet } from "./capability.js"; +export { Actor } from "./actor.js"; +export { type Lease, LeaseAuthority, leaseActive } from "./lease.js"; +export { type Tier, TierRegistry, DEFAULT_TIER_C } from "./tier.js"; +export { + type Action, + type Decision, + type MediationResult, + type MediatorOptions, + Mediator, +} from "./mediation.js"; +export { type AuditEntry, type AuditRecord, AuditLog } from "./audit.js"; +export { type IntentDeclaration, type Reconciliation, IntentContext } from "./intent.js"; diff --git a/conformance/src/protocol/intent.ts b/conformance/src/protocol/intent.ts new file mode 100644 index 0000000..21bc6ee --- /dev/null +++ b/conformance/src/protocol/intent.ts @@ -0,0 +1,40 @@ +/** + * Intent declaration (VISION §6; ADR-017 "intent declared once, actions + * auto-validated"). An actor declares an intent — a labelled scope of capability + * names with a TTL — once, at a boundary. Subsequent in-scope actions reconcile + * silently against it, which is what lets the routine path stay prompt-free + * (frictionlessness, ADR-017 C2) without abandoning per-action accountability: + * every action is still reconciled, just not re-approved. + */ +export interface IntentDeclaration { + readonly label: string; + readonly subject: string; + readonly scope: ReadonlySet; + readonly declaredAt: number; + readonly expiresAt: number; +} + +export type Reconciliation = "in-scope" | "out-of-scope" | "expired"; + +export class IntentContext { + constructor(private readonly now: () => number = () => Date.now()) {} + + /** Declare an intent over a set of capability names for `ttlMs`. */ + declare(subject: string, label: string, scope: Iterable, ttlMs: number): IntentDeclaration { + const declaredAt = this.now(); + return { + label, + subject, + scope: new Set(scope), + declaredAt, + expiresAt: declaredAt + ttlMs, + }; + } + + /** Reconcile an action's required capability against a declared intent. An + * in-scope, unexpired action needs no fresh approval; anything else escalates. */ + reconcile(intent: IntentDeclaration, capabilityName: string, at: number = this.now()): Reconciliation { + if (at >= intent.expiresAt) return "expired"; + return intent.scope.has(capabilityName) ? "in-scope" : "out-of-scope"; + } +} diff --git a/conformance/src/protocol/lease.ts b/conformance/src/protocol/lease.ts new file mode 100644 index 0000000..3d224b6 --- /dev/null +++ b/conformance/src/protocol/lease.ts @@ -0,0 +1,45 @@ +/** + * Lease issuance — where weight is paid (WEIGHTLESS.md amortization; ADR-017 + * standing capabilities). A lease binds a set of standing capabilities to a subject + * for a bounded window. The cost (validation, audit, the boundary handshake) is paid + * once here, at the boundary; the steady-state per-action path then has nothing left + * to check. At N actions per lease, per-action overhead trends to boundary_cost / N. + */ +import { CapabilitySet } from "./capability.js"; + +export interface Lease { + readonly subject: string; + readonly capabilities: CapabilitySet; + readonly issuedAt: number; + readonly expiresAt: number; +} + +export function leaseActive(lease: Lease, now: number): boolean { + return now >= lease.issuedAt && now < lease.expiresAt; +} + +/** + * Issues leases by attenuating an actor's held authority down to a requested scope. + * A lease can never carry authority the subject did not already hold — issuance is + * attenuating, never amplifying. + */ +export class LeaseAuthority { + constructor(private readonly now: () => number = () => Date.now()) {} + + /** Issue a lease over `scopeNames` (intersected with what `holder` holds) for + * `ttlMs`. This is the amortizable boundary event; everything downstream is free. */ + issue( + subject: string, + holder: CapabilitySet, + scopeNames: Iterable, + ttlMs: number, + ): Lease { + const issuedAt = this.now(); + return { + subject, + capabilities: holder.attenuate(scopeNames), + issuedAt, + expiresAt: issuedAt + ttlMs, + }; + } +} diff --git a/conformance/src/protocol/mediation.ts b/conformance/src/protocol/mediation.ts new file mode 100644 index 0000000..815fb37 --- /dev/null +++ b/conformance/src/protocol/mediation.ts @@ -0,0 +1,78 @@ +/** + * Mediation — the interception point (ADR-007), applied structurally. + * + * The load-bearing property: on the routine (Tier A/B) path, a held capability is + * passed through with *no boundary step* and the payload *untouched* — the decision + * was made when the reference was granted, so there is nothing to evaluate now + * (no-delay, no-bandwidth, no-data-alteration). The explicit boundary is invoked only + * at Tier C, where interference is the intended function. An unheld capability yields + * UNNAMEABLE — an absence, never a DENIED decision (which would imply the action was + * nameable and evaluated, i.e. procedural — the very thing ADR-020 Group W forbids). + */ +import type { Actor } from "./actor.js"; +import type { TierRegistry } from "./tier.js"; +import type { AuditLog } from "./audit.js"; + +export type Decision = "ALLOWED" | "UNNAMEABLE" | "DENIED"; + +export interface Action { + readonly capability: string; + readonly payload: Uint8Array; +} + +export interface MediationResult { + readonly decision: Decision; + /** Whether the explicit Tier-C boundary was invoked on this action. */ + readonly boundaryInvoked: boolean; + /** The payload as delivered to the handler — byte-identical to input on the + * routine path (the no-data-alteration floor, ADR-007). */ + readonly payloadDelivered: Uint8Array; +} + +export interface MediatorOptions { + /** Optional append-only audit log; every decision is recorded if present. The + * record is a *local* append, off the action's critical path (WEIGHTLESS.md). */ + readonly audit?: AuditLog; + /** Clock for audit timestamps; defaults to wall time. */ + readonly now?: () => number; +} + +export class Mediator { + private readonly audit?: AuditLog; + private readonly now: () => number; + + constructor( + private readonly tiers: TierRegistry, + options: MediatorOptions = {}, + ) { + this.audit = options.audit; + this.now = options.now ?? (() => Date.now()); + } + + mediate(actor: Actor, action: Action): MediationResult { + const tier = this.tiers.classify(action.capability); + + let result: MediationResult; + if (!actor.canName(action.capability)) { + // No reference → the action is not expressible. Absence, not a denied check. + result = { decision: "UNNAMEABLE", boundaryInvoked: false, payloadDelivered: action.payload }; + } else if (tier === "C") { + // The ~2% boundary: the explicit gate runs; interference is the point here. + result = { decision: "ALLOWED", boundaryInvoked: true, payloadDelivered: action.payload }; + } else { + // Routine path: structural pass-through. No step added, payload untouched. + result = { decision: "ALLOWED", boundaryInvoked: false, payloadDelivered: action.payload }; + } + + this.audit?.append({ + subject: actor.id, + capability: action.capability, + tier, + decision: result.decision, + boundaryInvoked: result.boundaryInvoked, + at: this.now(), + }); + + return result; + } +} diff --git a/conformance/src/protocol/tier.ts b/conformance/src/protocol/tier.ts new file mode 100644 index 0000000..4ea00a2 --- /dev/null +++ b/conformance/src/protocol/tier.ts @@ -0,0 +1,48 @@ +/** + * Risk-tier classification (ADR-017 C2; WEIGHTLESS.md "concentrated scope"). + * + * Tier A (~90%) and B (~8%) are the routine path — literal-zero, structural. + * Tier C (~2%) is the deliberate-weight boundary (production deploy, secret access, + * capability escalation, irreversible destruction) where interference is the + * intended function. The whole weightlessness discipline is making A/B cover the + * dominant path and concentrating cost at C. + */ +export type Tier = "A" | "B" | "C"; + +/** Maps a capability name to its tier. Unregistered names default to the routine + * path (Tier A): a capability is only "heavy" if explicitly declared so. */ +export class TierRegistry { + private readonly byName = new Map(); + + /** Register one capability name at a tier. Returns `this` for chaining. */ + register(name: string, tier: Tier): this { + this.byName.set(name, tier); + return this; + } + + /** Register many names at one tier. */ + registerAll(names: Iterable, tier: Tier): this { + for (const n of names) this.register(n, tier); + return this; + } + + classify(capabilityName: string): Tier { + return this.byName.get(capabilityName) ?? "A"; + } + + /** True iff the capability is on the routine (literal-zero) path. */ + isRoutine(capabilityName: string): boolean { + return this.classify(capabilityName) !== "C"; + } +} + +/** A conventional Tier-C default set for developer-workflow deployments (ADR-017 + * names exactly these as the high-stakes operations). */ +export const DEFAULT_TIER_C: readonly string[] = [ + "secret.read", + "secret.ssh-key.read", + "deploy.staging", + "deploy.production", + "capability.escalate", + "fs.destroy", +]; diff --git a/conformance/src/setup.ts b/conformance/src/setup.ts new file mode 100644 index 0000000..dec5b46 --- /dev/null +++ b/conformance/src/setup.ts @@ -0,0 +1,13 @@ +/** + * vitest setup: if ASHE_CONFORMANCE_ADAPTER names a module, import it and register + * its default export as the global adapter. Otherwise leave it unset, and every + * group skips. Kept dependency-free (no path module) so the scaffold runs anywhere. + */ +export {}; // ensure module scope so top-level await is permitted + +const ref = process.env.ASHE_CONFORMANCE_ADAPTER; +if (ref) { + const specifier = ref.startsWith(".") || ref.startsWith("/") ? ref : `./${ref}`; + const mod = (await import(specifier)) as { default?: unknown; adapter?: unknown }; + (globalThis as { __ASHE_ADAPTER__?: unknown }).__ASHE_ADAPTER__ = mod.default ?? mod.adapter; +} diff --git a/conformance/tests/group-h-how.test.ts b/conformance/tests/group-h-how.test.ts new file mode 100644 index 0000000..89c224c --- /dev/null +++ b/conformance/tests/group-h-how.test.ts @@ -0,0 +1,46 @@ +import { describe, it, expect } from "vitest"; +import { requireAdapter, type ActionDescriptor } from "../src/adapter.js"; + +const adapter = requireAdapter(); +const group = adapter ? describe : describe.skip; + +const ROUTINE: ActionDescriptor = { + id: "write-source", + requiredCapability: "code.write", + expectedTier: "A", + payload: new Uint8Array([10, 20, 30, 40]), +}; + +// ADR-020 Group H — how (structural mechanism). +group("Group H — how (structural mechanism)", () => { + it("H1-no-added-step: no ASHE evaluation step on the routine path when claiming structural", () => { + const a = adapter!; + const layer = a.routinePathLayer(); + const { stepInterposed } = a.routineActionTrace(ROUTINE); + if (layer >= 2) { + // Structural claim (Layer 2/3/4): the substrate is the boundary, no added step. + expect(stepInterposed).toBe(false); + } else { + // Layer 1 is procedural by construction; honesty is enforced by H3, not here. + expect(layer).toBe(1); + } + }); + + it("H2-byte-identity: routine payload arrives byte-identical to the unmediated baseline", () => { + const a = adapter!; + const { payloadDelivered } = a.routineActionTrace(ROUTINE); + const baseline = a.unmediatedBaselinePayload(ROUTINE); + expect(Array.from(payloadDelivered)).toEqual(Array.from(baseline)); + }); + + it("H3-layer-disclosure: a procedural (Layer 1) routine path must not claim literal-zero", () => { + const a = adapter!; + if (a.routinePathLayer() === 1) { + expect(a.declaredGrade("no-delay")).toBe("amortized-small"); + expect(a.declaredGrade("no-bandwidth")).toBe("amortized-small"); + } else { + // Structural layers may legitimately claim literal-zero; nothing to disclose. + expect(["literal-zero", "amortized-small"]).toContain(a.declaredGrade("no-delay")); + } + }); +}); diff --git a/conformance/tests/group-n-when.test.ts b/conformance/tests/group-n-when.test.ts new file mode 100644 index 0000000..6c99f59 --- /dev/null +++ b/conformance/tests/group-n-when.test.ts @@ -0,0 +1,28 @@ +import { describe, it, expect } from "vitest"; +import { requireAdapter, type ActionDescriptor } from "../src/adapter.js"; + +const adapter = requireAdapter(); +const group = adapter ? describe : describe.skip; + +const GUARDED: ActionDescriptor = { + id: "deploy-production", + requiredCapability: "deploy.production", + expectedTier: "C", + payload: new Uint8Array([99]), +}; + +// ADR-020 Group N — when (at construction). +group("Group N — when (at construction)", () => { + it("N1-construction-order: the perimeter exists before contents are reachable", () => { + const a = adapter!; + expect(a.perimeterEstablishedAtConstruction()).toBe(true); + }); + + it("N2-no-front-gate: disabling ASHE makes a guarded action unreachable, not merely ungated", () => { + const a = adapter!; + // The sharpest test of the facet: if removing the component restores direct + // access, the boundary was a removable front gate (bolted on), not the shape. + expect(a.guardedReachabilityWithAsheDisabled(GUARDED)).toBe("unreachable"); + expect(a.guardedReachabilityWithAsheDisabled(GUARDED)).not.toBe("reachable"); + }); +}); diff --git a/conformance/tests/group-r-where.test.ts b/conformance/tests/group-r-where.test.ts new file mode 100644 index 0000000..1102085 --- /dev/null +++ b/conformance/tests/group-r-where.test.ts @@ -0,0 +1,53 @@ +import { describe, it, expect } from "vitest"; +import { requireAdapter, type ActionDescriptor } from "../src/adapter.js"; + +const adapter = requireAdapter(); +const group = adapter ? describe : describe.skip; + +const ROUTINE: ActionDescriptor = { + id: "run-tests", + requiredCapability: "test.run", + expectedTier: "A", + payload: new Uint8Array([1]), +}; +const TIER_C: ActionDescriptor = { + id: "read-secret", + requiredCapability: "secret.read", + expectedTier: "C", + payload: new Uint8Array([2]), +}; + +// A representative routine workload for the friction-frequency measurement. +const WORKLOAD: ActionDescriptor[] = Array.from({ length: 100 }, (_, i) => ({ + id: `routine-${i}`, + requiredCapability: "code.read", + expectedTier: "A" as const, + payload: new Uint8Array([i & 0xff]), +})); + +// ADR-020 Group R — where (concentrated scope). +group("Group R — where (concentrated scope)", () => { + it("R1-path-classification: routine and Tier C are classified distinctly", () => { + const a = adapter!; + expect(a.classifyTier(ROUTINE)).not.toBe("C"); + expect(a.classifyTier(TIER_C)).toBe("C"); + }); + + it("R2-no-uniform-enforcement: the 98% is not gated like the 2%", () => { + const a = adapter!; + const routine = a.costProfile(ROUTINE); + expect(routine.roundTrip).toBe(false); + expect(routine.addedTokens).toBe(0); + expect(routine.humanPrompt).toBe(false); + + // The Tier C boundary, by contrast, DOES invoke the explicit boundary — + // interference there is the intended function, not a violation. + const tierC = a.costProfile(TIER_C); + expect(tierC.roundTrip || tierC.humanPrompt).toBe(true); + }); + + it("R3-friction-frequency: routine-path prompt frequency stays below the declared threshold", () => { + const a = adapter!; + expect(a.routinePromptFrequency(WORKLOAD)).toBeLessThan(a.promptFrequencyThreshold()); + }); +}); diff --git a/conformance/tests/group-w-what.test.ts b/conformance/tests/group-w-what.test.ts new file mode 100644 index 0000000..ce267f4 --- /dev/null +++ b/conformance/tests/group-w-what.test.ts @@ -0,0 +1,54 @@ +import { describe, it, expect } from "vitest"; +import { requireAdapter, type ActionDescriptor } from "../src/adapter.js"; + +const adapter = requireAdapter(); +const group = adapter ? describe : describe.skip; + +const SSH: ActionDescriptor = { + id: "read-ssh-key", + requiredCapability: "secret.ssh-key.read", + expectedTier: "C", + payload: new Uint8Array([1, 2, 3]), +}; +const READ: ActionDescriptor = { + id: "read-source", + requiredCapability: "code.read", + expectedTier: "A", + payload: new Uint8Array([4, 5, 6]), +}; + +// ADR-020 Group W — what (object-capability primitive). +group("Group W — what (object-capability primitive)", () => { + it("W1-unnameability: an unauthorized action is UNNAMEABLE, never DENIED", () => { + const a = adapter!; + // A JPEG-parser-like actor holds the read capability but never the SSH-key one. + const actor = a.makeActor([READ.requiredCapability]); + expect(a.canName(actor, SSH)).toBe(false); + // The load-bearing assertion: absence, not denial. A denial decision means the + // action was nameable and evaluated — that is procedural, and fails the facet. + expect(a.invoke(actor, SSH)).toBe("UNNAMEABLE"); + expect(a.invoke(actor, SSH)).not.toBe("DENIED"); + }); + + it("W2-zero-ambient-authority: reachable authority equals exactly the granted set", () => { + const a = adapter!; + const grants = [READ.requiredCapability]; + const actor = a.makeActor(grants); + const reachable = a.reachableAuthorities(actor).sort(); + expect(reachable).toEqual([...grants].sort()); + }); + + it("W3-attenuation: a sub-actor cannot exceed the granting actor", () => { + const a = adapter!; + const parent = a.makeActor([READ.requiredCapability]); + // Attempt to grant the child more than the parent holds. + const child = a.spawnSubActor(parent, [ + READ.requiredCapability, + SSH.requiredCapability, + ]); + const parentSet = new Set(a.reachableAuthorities(parent)); + for (const cap of a.reachableAuthorities(child)) { + expect(parentSet.has(cap)).toBe(true); + } + }); +}); diff --git a/conformance/tests/protocol/actor.test.ts b/conformance/tests/protocol/actor.test.ts new file mode 100644 index 0000000..cdc1467 --- /dev/null +++ b/conformance/tests/protocol/actor.test.ts @@ -0,0 +1,28 @@ +import { describe, it, expect } from "vitest"; +import { Actor, CapabilityIssuer, CapabilitySet } from "../../src/protocol/index.js"; + +describe("Actor — cascade attenuation is structural", () => { + const issuer = new CapabilityIssuer(); + const parent = new Actor( + "parent", + new CapabilitySet([issuer.mint("code.read"), issuer.mint("code.write")]), + ); + + it("a sub-actor cannot exceed the parent — excess names are simply not constructed", () => { + const child = parent.spawn("child", ["code.read", "deploy.production"]); + expect(child.reachableAuthorities().sort()).toEqual(["code.read"]); + expect(child.canName("deploy.production")).toBe(false); + }); + + it("reachable authority equals exactly the held set (zero ambient)", () => { + expect(parent.reachableAuthorities().sort()).toEqual(["code.read", "code.write"]); + }); + + it("grandchildren attenuate monotonically down the cascade", () => { + const child = parent.spawn("child", ["code.read", "code.write"]); + const grandchild = child.spawn("grandchild", ["code.write"]); + expect(grandchild.reachableAuthorities()).toEqual(["code.write"]); + // Cannot re-acquire code.read it was not handed. + expect(grandchild.spawn("ggc", ["code.read"]).reachableAuthorities()).toEqual([]); + }); +}); diff --git a/conformance/tests/protocol/audit.test.ts b/conformance/tests/protocol/audit.test.ts new file mode 100644 index 0000000..7cb2747 --- /dev/null +++ b/conformance/tests/protocol/audit.test.ts @@ -0,0 +1,57 @@ +import { describe, it, expect } from "vitest"; +import { + Actor, + AuditLog, + CapabilityIssuer, + CapabilitySet, + DEFAULT_TIER_C, + Mediator, + TierRegistry, +} from "../../src/protocol/index.js"; + +describe("AuditLog — tamper-evident hash chain", () => { + it("links records and verifies a clean chain", () => { + const log = new AuditLog(); + log.append({ subject: "a", capability: "code.read", tier: "A", decision: "ALLOWED", boundaryInvoked: false, at: 1 }); + log.append({ subject: "a", capability: "deploy.production", tier: "C", decision: "ALLOWED", boundaryInvoked: true, at: 2 }); + expect(log.length).toBe(2); + expect(log.verify()).toBe(true); + expect(log.entries()[1]!.prevHash).toBe(log.entries()[0]!.hash); + expect(log.head).toBe(log.entries()[1]!.hash); + }); + + it("detects a post-hoc edit of a sealed record", () => { + const log = new AuditLog(); + log.append({ subject: "a", capability: "code.read", tier: "A", decision: "ALLOWED", boundaryInvoked: false, at: 1 }); + log.append({ subject: "a", capability: "secret.read", tier: "C", decision: "UNNAMEABLE", boundaryInvoked: false, at: 2 }); + // Forge: rewrite history so the UNNAMEABLE attempt reads as ALLOWED. + (log.entries()[1] as { decision: string }).decision = "ALLOWED"; + expect(log.verify()).toBe(false); + }); +}); + +describe("Mediator — audit-by-construction", () => { + const issuer = new CapabilityIssuer(); + const tiers = new TierRegistry().registerAll(DEFAULT_TIER_C, "C"); + const actor = new Actor("dev", new CapabilitySet([issuer.mint("code.write")])); + const payload = new Uint8Array([1]); + + it("every mediation emits one record, including unnameable attempts", () => { + let clock = 100; + const audit = new AuditLog(); + const mediator = new Mediator(tiers, { audit, now: () => clock++ }); + + mediator.mediate(actor, { capability: "code.write", payload }); // ALLOWED routine + mediator.mediate(actor, { capability: "secret.read", payload }); // UNNAMEABLE + + expect(audit.length).toBe(2); + expect(audit.entries()[0]!.decision).toBe("ALLOWED"); + expect(audit.entries()[1]!.decision).toBe("UNNAMEABLE"); + expect(audit.verify()).toBe(true); + }); + + it("is opt-in: no audit log means no recording, decisions unchanged", () => { + const mediator = new Mediator(tiers); + expect(mediator.mediate(actor, { capability: "code.write", payload }).decision).toBe("ALLOWED"); + }); +}); diff --git a/conformance/tests/protocol/capability.test.ts b/conformance/tests/protocol/capability.test.ts new file mode 100644 index 0000000..7041b4f --- /dev/null +++ b/conformance/tests/protocol/capability.test.ts @@ -0,0 +1,48 @@ +import { describe, it, expect } from "vitest"; +import { Capability, CapabilityIssuer, CapabilitySet } from "../../src/protocol/index.js"; + +describe("Capability — unforgeability", () => { + it("cannot be constructed from outside the module (no mint token)", () => { + // The mint token is module-private, so any externally-supplied symbol is rejected + // at runtime — capabilities can only be issued, never forged. + expect(() => new Capability(Symbol("fake"), "deploy.production", "x")).toThrow(/unforgeable/); + }); + + it("an issuer mints distinct references even for the same name", () => { + const issuer = new CapabilityIssuer(); + const a = issuer.mint("code.read"); + const b = issuer.mint("code.read"); + expect(a).not.toBe(b); + expect(a.id).not.toEqual(b.id); + }); +}); + +describe("CapabilitySet — attenuation only, never amplification", () => { + const issuer = new CapabilityIssuer(); + const read = issuer.mint("code.read"); + const write = issuer.mint("code.write"); + const set = new CapabilitySet([read, write]); + + it("has() reflects held names", () => { + expect(set.has("code.read")).toBe(true); + expect(set.has("secret.read")).toBe(false); + }); + + it("holds() is identity-based, not name-based", () => { + const impostor = issuer.mint("code.read"); + expect(set.holds(read)).toBe(true); + expect(set.holds(impostor)).toBe(false); + }); + + it("attenuate() can only drop authority, never add it", () => { + const narrowed = set.attenuate(["code.read", "secret.read"]); + expect(narrowed.names().sort()).toEqual(["code.read"]); // secret.read was never held + expect(narrowed.size).toBe(1); + }); + + it("contains() recognises a strict attenuation as a subset", () => { + const narrowed = set.attenuate(["code.read"]); + expect(set.contains(narrowed)).toBe(true); + expect(narrowed.contains(set)).toBe(false); + }); +}); diff --git a/conformance/tests/protocol/intent.test.ts b/conformance/tests/protocol/intent.test.ts new file mode 100644 index 0000000..0a696bc --- /dev/null +++ b/conformance/tests/protocol/intent.test.ts @@ -0,0 +1,27 @@ +import { describe, it, expect } from "vitest"; +import { IntentContext } from "../../src/protocol/index.js"; + +describe("IntentContext — declare once, reconcile silently", () => { + it("in-scope actions reconcile without escalation", () => { + let clock = 0; + const ctx = new IntentContext(() => clock); + const intent = ctx.declare("dev", "auth refactor", ["code.read", "code.write", "test.run"], 3_600_000); + expect(ctx.reconcile(intent, "code.write", 1_000)).toBe("in-scope"); + expect(ctx.reconcile(intent, "test.run", 1_000)).toBe("in-scope"); + }); + + it("out-of-scope actions escalate rather than ride the declaration", () => { + let clock = 0; + const ctx = new IntentContext(() => clock); + const intent = ctx.declare("dev", "auth refactor", ["code.read", "code.write"], 3_600_000); + expect(ctx.reconcile(intent, "deploy.production", 1_000)).toBe("out-of-scope"); + }); + + it("an expired declaration no longer covers even in-scope actions", () => { + let clock = 0; + const ctx = new IntentContext(() => clock); + const intent = ctx.declare("dev", "auth refactor", ["code.write"], 5_000); + expect(ctx.reconcile(intent, "code.write", 4_999)).toBe("in-scope"); + expect(ctx.reconcile(intent, "code.write", 5_000)).toBe("expired"); + }); +}); diff --git a/conformance/tests/protocol/lease.test.ts b/conformance/tests/protocol/lease.test.ts new file mode 100644 index 0000000..2db84f3 --- /dev/null +++ b/conformance/tests/protocol/lease.test.ts @@ -0,0 +1,35 @@ +import { describe, it, expect } from "vitest"; +import { + CapabilityIssuer, + CapabilitySet, + LeaseAuthority, + leaseActive, +} from "../../src/protocol/index.js"; + +describe("LeaseAuthority — boundary-amortized authority", () => { + const issuer = new CapabilityIssuer(); + const holder = new CapabilitySet([ + issuer.mint("code.read"), + issuer.mint("code.write"), + issuer.mint("test.run"), + ]); + + it("a lease carries at most the holder's authority, attenuated to scope", () => { + let clock = 1_000; + const authority = new LeaseAuthority(() => clock); + const lease = authority.issue("session-1", holder, ["code.read", "deploy.production"], 5_000); + // deploy.production was never held → not leasable. + expect(lease.capabilities.names().sort()).toEqual(["code.read"]); + expect(lease.subject).toBe("session-1"); + }); + + it("leaseActive() respects the issued/expiry window", () => { + let clock = 1_000; + const authority = new LeaseAuthority(() => clock); + const lease = authority.issue("session-2", holder, ["test.run"], 5_000); + expect(leaseActive(lease, 1_000)).toBe(true); + expect(leaseActive(lease, 5_999)).toBe(true); + expect(leaseActive(lease, 6_000)).toBe(false); // expiry is exclusive + expect(leaseActive(lease, 999)).toBe(false); + }); +}); diff --git a/conformance/tests/protocol/mediation.test.ts b/conformance/tests/protocol/mediation.test.ts new file mode 100644 index 0000000..8ea4c72 --- /dev/null +++ b/conformance/tests/protocol/mediation.test.ts @@ -0,0 +1,42 @@ +import { describe, it, expect } from "vitest"; +import { + Actor, + CapabilityIssuer, + CapabilitySet, + DEFAULT_TIER_C, + Mediator, + TierRegistry, +} from "../../src/protocol/index.js"; + +const issuer = new CapabilityIssuer(); +const tiers = new TierRegistry().registerAll(DEFAULT_TIER_C, "C"); +const mediator = new Mediator(tiers); + +const dev = new Actor( + "dev", + new CapabilitySet([issuer.mint("code.write"), issuer.mint("deploy.production")]), +); + +const payload = new Uint8Array([7, 8, 9]); + +describe("Mediator — structural routine path, explicit Tier-C boundary", () => { + it("routine (Tier A) held action: allowed, no boundary step, payload untouched", () => { + const r = mediator.mediate(dev, { capability: "code.write", payload }); + expect(r.decision).toBe("ALLOWED"); + expect(r.boundaryInvoked).toBe(false); + expect(Array.from(r.payloadDelivered)).toEqual(Array.from(payload)); + }); + + it("Tier-C held action: allowed, but the explicit boundary is invoked", () => { + const r = mediator.mediate(dev, { capability: "deploy.production", payload }); + expect(r.decision).toBe("ALLOWED"); + expect(r.boundaryInvoked).toBe(true); + }); + + it("unheld capability is UNNAMEABLE, never DENIED (absence, not a denied check)", () => { + const r = mediator.mediate(dev, { capability: "secret.read", payload }); + expect(r.decision).toBe("UNNAMEABLE"); + expect(r.decision).not.toBe("DENIED"); + expect(r.boundaryInvoked).toBe(false); + }); +}); diff --git a/conformance/tsconfig.json b/conformance/tsconfig.json new file mode 100644 index 0000000..51b0525 --- /dev/null +++ b/conformance/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022"], + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitOverride": true, + "esModuleInterop": true, + "skipLibCheck": true, + "types": ["node", "vitest/globals"] + }, + "include": ["src", "tests", "vitest.config.ts"] +} diff --git a/conformance/vitest.config.ts b/conformance/vitest.config.ts new file mode 100644 index 0000000..e11bf24 --- /dev/null +++ b/conformance/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + globals: true, + include: ["tests/**/*.test.ts"], + // Loads the adapter named by ASHE_CONFORMANCE_ADAPTER (if any) before the suite runs. + setupFiles: ["./src/setup.ts"], + }, +}); diff --git a/decisions/ADR-020-weightlessness-proper-application-conformance.md b/decisions/ADR-020-weightlessness-proper-application-conformance.md new file mode 100644 index 0000000..a5b1c34 --- /dev/null +++ b/decisions/ADR-020-weightlessness-proper-application-conformance.md @@ -0,0 +1,131 @@ +# ADR-020: Weightlessness is conformant only under proper application + +| Field | Value | +|-------|-------| +| Status | Accepted | +| Date | 2026-05-30 | +| Decider | PK + Claude | +| Touches | protocol (primary — defines a conformance gate on the weightlessness claim), reference-arch, paradigm | +| Cited by | WEIGHTLESS.md ("Proper application — the resolving discipline") | +| Builds on | ADR-007 (interception-chain — idempotent, no dispatch-state mutation), ADR-009 (deployment profiles — graceful degradation), ADR-014 (phased enforcement — the Layer 1→4 trajectory along which structural application deepens), ADR-015 (validation methodology — evidence grades for performance claims), ADR-017 (sealed workspace — wall-up-first, `ashe workspace init` as step 1; the frictionlessness mandate) | + +## Context + +[WEIGHTLESS.md](../WEIGHTLESS.md) names ASHE's central UX promise — "invisible most of the time, explicit only at risk boundaries" — as a single engineering property and states it strictly as **four hard invariants** on the steady-state path: no delay, no bandwidth, no data alteration, no interference. Two (no data alteration, no interference) are already floors — contractual via ADR-007 and constructional via ADR-009/ADR-018. The other two (no delay, no bandwidth) are presented as *targets* reachable only as the enforcement layer deepens toward structural mediation. + +The document also names the honest tension: **active enforcement is interference by definition** — the instant ASHE blocks an action it has interfered, and if it blocks synchronously it has delayed. Literal-zero is therefore not claimable for *every* action; it is claimable for pre-authorized actions (the decision was made at issuance, before the action existed) and structurally-bounded denials (the action was never expressible). Weightlessness is the discipline of making those two categories cover the ~98% routine path while deliberately concentrating weight at the ~2% Tier C boundary where interference is the intended function. + +What the corpus has *not* until now stated normatively is the **failure mode**: weightlessness is not a mechanism an implementation installs and thereby acquires. It is the default state of a system whose authority boundaries were applied correctly, and it is **forfeited the instant they are applied incorrectly**. A misapplied ASHE is not a lighter middleware — it is just middleware. The property holds or it does not, depending entirely on *how* the boundary was applied. Concretely, the same four invariants collapse under four distinct misapplications: + +| Misapplication | Why weight reappears | +|---|---| +| Wrong primitive — permission flags checked against an identity instead of object-capability held-or-absent authority | A lookup runs on every action; an "unauthorized" action is still *nameable*, so it must be evaluated rather than being structurally absent | +| Procedural instead of structural — a check ASHE executes per action instead of the substrate's own mechanism being the boundary | A step sits on the hot path forever; it can be made fast but never literally zero | +| Bolted on instead of built in — a gate placed in front of an already-built system instead of the system being shaped around the boundary at construction | The gate is a check that always runs; nothing was made structurally absent | +| Uniform scope instead of concentrated — the ~98% routine path enforced the same way as the ~2% Tier C boundary | Every routine action becomes a gated action; the amortization that makes weight invisible never happens | + +Without a normative gate, an implementation could optimize a per-action check until it is sub-millisecond and claim "weightless." That claim is false in the strict sense the document defines: **a fast check is still a check, still non-zero, still weight.** Weightlessness is not achieved by accelerating the work; it is achieved by applying the boundary such that there is no work on the path that matters. The corpus needs to say so as a conformance requirement, not only as prose. + +## Decision + +**An ASHE implementation MAY claim the weightlessness property — the four hard invariants of [WEIGHTLESS.md](../WEIGHTLESS.md) on the steady-state path — only if it satisfies all four facets of proper application. The four facets are one indivisible discipline: drop any one and the claim is non-conformant.** + +### The four facets (all required, conjunctively) + +| Facet | Normative requirement | +|---|---| +| **What — the primitive** | Authority MUST be modeled as an object-capability: *held or absent*, with zero ambient authority. An action the actor is not authorized to take MUST be *unnameable* (no reference to invoke), not *checked-and-denied* (a flag evaluated at action time). | +| **How — the mechanism** | The routine-path boundary MUST be **structural** — the substrate's own existing mechanism (type system at Layer 2, OS capability / MMU at Layer 3, hardware root at Layer 4, per [ADR-014](ADR-014-phased-enforcement-model.md)) — not a **procedural** check ASHE executes as an added step on the path. At Layer 1 (cooperating SDK), where mediation is necessarily procedural, the implementation MUST disclose that no-delay/no-bandwidth are amortized-small rather than literal-zero (see Layer-1 caveat below). | +| **When — the moment** | The boundary MUST be established **at construction**, not bolted on after the fact. Per [ADR-017](ADR-017-sealed-workspace-foundational-dev-pattern.md), the wall goes up first and development happens inside it (`ashe workspace init` as step 1, the way `git init` is today). A gate placed in front of an already-built system does not satisfy this facet. | +| **Where — the scope** | Literal-zero MUST be applied to the dominant routine path (the ~98%, Tier A/B), with deliberate weight concentrated at the ~2% Tier C boundary (production deploys, secret access, capability escalation, irreversible destruction) where interference is the intended function. Uniform enforcement that treats the 98% like the 2% does not satisfy this facet. | + +The four compose into a single test: **apply the object-capability primitive (what), structurally (how), at construction (when), to the dominant path (where) — and the four invariants hold for free, because the substrate already does the work and the decision was made before the action existed.** Miss any one facet and weight reappears in the corresponding column of the Context table above. + +### What the gate forbids + +The following claims are **non-conformant** and MUST NOT be made: + +- Claiming "weightless" on the basis of an optimized per-action check (a fast check is still a check; this fails the *how* facet). +- Claiming "weightless" for a permission-flag / identity-lookup model (fails the *what* facet — the action remains nameable and must be evaluated). +- Claiming "weightless" for a gate retrofitted in front of an existing system (fails the *when* facet). +- Claiming "weightless" while enforcing the routine path identically to the Tier C boundary (fails the *where* facet). +- Claiming literal-zero (no delay / no bandwidth) at Layer 1 cooperating-SDK enforcement without disclosing that those two invariants are amortized-small, not zero, at that layer (see caveat). + +### Layer-1 caveat — honest disclosure, not disqualification + +A Layer 1 (cooperating SDK) implementation cannot make the routine-path boundary fully structural — mediation there is procedural and pays a small, sub-millisecond, amortized cost (WEIGHTLESS §1). Such an implementation is **not** thereby barred from the weightlessness *trajectory*; it MUST simply disclose, per [ADR-015](ADR-015-validation-methodology-and-tiered-claims.md) evidence-grade discipline, that no-delay and no-bandwidth are **amortized-small (Layer 1)** rather than **literal-zero (Layer 3/4 structural property)**. The two already-floored invariants (no data alteration via ADR-007, no interference via ADR-009/ADR-018) hold at every layer. The distinction this ADR enforces is between *honest amortized-small under partial structural application* and *false literal-zero claimed for a procedural check*. + +## Consequences + +**What becomes easier:** + +- **The weightlessness claim becomes falsifiable.** "Weightless" stops being a marketing adjective and becomes a four-part conformance predicate that a suite can test (see below). A reviewer can reject a claim by pointing at the failing facet. +- **The middleware failure mode is named and gated.** The most likely way an implementation degenerates — building a fast per-action check and calling it weightless — is now explicitly non-conformant, so it surfaces in conformance review rather than shipping. +- **Layer-1 honesty is structural.** Implementations on the cooperating-SDK layer get a sanctioned, non-disqualifying way to describe their cost (amortized-small, disclosed) instead of either over-claiming or being excluded. + +**What becomes harder:** + +- **Retrofit claims.** An adopter who bolts ASHE onto an existing system as a front gate cannot claim weightlessness without satisfying the *when* facet — which for legacy systems means the ADR-017 migration path (`ashe workspace import` → role mapping → progressive Layer-2+ engagement), not a one-line wrapper. +- **Single-layer marketing.** A vendor cannot advertise literal-zero on the strength of a benchmarked-fast Layer-1 check; the evidence-grade disclosure is mandatory. + +**What becomes possible:** + +- **Conformance-graded weightlessness claims.** Combined with ADR-014's layer model and ADR-015's evidence grades, a deployment can state precisely *which* invariants are literal-zero (structural, Layer 3/4) versus amortized-small (procedural, Layer 1), per facet — a graded, defensible claim rather than a binary boast. + +**What becomes impossible (intentionally):** + +- Claiming weightlessness for any of the four misapplications in the Context table. +- Treating weightlessness as a feature to install rather than a property of correct application — the ADR makes "proper application" the precondition, not an optimization applied afterward. + +## Alternatives Considered + +**1. Leave "proper application" as prose in WEIGHTLESS.md (design-note principle only).** Rejected. Prose describes the discipline but does not bind a conformance suite; the most common degeneration (fast-check-called-weightless) needs a normative gate to be rejectable, not just discouraged. PK explicitly approved promotion to an ADR. + +**2. Make weightlessness a single-lever requirement (pick the strongest facet — structural mechanism — and require only that).** Rejected. The facets are not substitutes; each closes a distinct leak (wrong primitive → lookup; procedural → path step; late → gate; uniform → per-action check). Requiring only *how* would admit a structurally-mediated system that still checks the 98% like the 2% (fails *where*) or that was bolted on (fails *when*). The conjunction is the point. + +**3. Disqualify Layer 1 from any weightlessness claim (reserve the term for Layer 3/4 structural enforcement only).** Rejected as too strict and at odds with ADR-014's phased trajectory. Layer 1 is the on-ramp; barring it from the vocabulary entirely would push honest implementers toward over-claiming rather than disclosing. The chosen path — amortized-small with mandatory disclosure — keeps Layer 1 on the trajectory while preventing false literal-zero. + +**4. Fold this into ADR-017's frictionlessness commitment rather than a new ADR.** Rejected. ADR-017's frictionlessness mandate governs the *cognitive/friction* axis (no per-action approval prompts) for the sealed-workspace dev pattern; this ADR governs the *whole* weightlessness claim (all four invariants, all axes) across every surface, and defines a conformance gate on the term itself. Different scope; ADR-017 is a `Builds on`, not a container. + +## Related decisions + +- ADR-007 — Interception-chain pattern. Supplies the **no data alteration** floor (idempotent, no dispatch-state mutation); the *how* facet's procedural-at-Layer-1 mediation rides the interception chain. +- ADR-009 — Deployment profiles. Supplies the **no interference** floor (graceful degradation never fails the host). +- ADR-014 — Phased enforcement model. Defines the Layer 1→4 trajectory along which the *how* facet moves from procedural (amortized-small) to structural (literal-zero); this ADR's Layer-1 caveat is grounded here. +- ADR-015 — Validation methodology and tiered claims. The evidence-grade discipline this ADR invokes for the mandatory amortized-small-vs-literal-zero disclosure. +- ADR-017 — Sealed workspace. Supplies the *when* facet (wall-up-first, `ashe workspace init` as step 1) and the migration path for retrofit; its frictionlessness mandate is the cognitive-axis instance of this ADR's general gate. + +## Conformance suite for the weightlessness gate + +A claim of weightlessness is verified by four test groups, one per facet (per [ADR-015](ADR-015-validation-methodology-and-tiered-claims.md) methodology). **All four groups MUST pass for the claim to be conformant** — they are conjunctive, mirroring the facets. Each group also records the layer at which the property holds, so the result is graded (literal-zero at Layer 3/4 vs amortized-small-disclosed at Layer 1), never binary-by-assertion. + +**Group W (what — object-capability primitive):** + +- **Unnameability test** — an actor lacking a capability MUST have no reference by which to invoke the guarded action; the test confirms the action is *absent from the actor's surface*, not present-and-rejected. A model that returns a denial decision for the unauthorized action *fails* this group (denial implies the action was nameable and evaluated). +- **Zero-ambient-authority test** — enumerate every authority reachable by a freshly-constructed actor; it MUST equal exactly the explicitly-granted set, with no implicit/ambient authority recoverable via path traversal, absolute paths, subprocess, or reflection. +- **Attenuation test** — a sub-actor's reachable authority MUST be a subset of the granting actor's (composes with ADR-017 cascade attenuation). + +**Group H (how — structural mechanism):** + +- **No-added-step test** — on the routine (Tier A/B) path, instrument the call site and confirm a passing action reaches its handler with **no ASHE-executed evaluation step interposed** at the claimed structural layer. The boundary MUST be the substrate's own mechanism (type system / OS capability / MMU / hardware root per [ADR-014](ADR-014-phased-enforcement-model.md)). +- **Byte-identity test** — a passing routine action MUST arrive at its handler byte-identical to the unmediated baseline (composes with the ADR-007 no-data-alteration floor); confirms no rewrite/transform on the path. +- **Layer-disclosure test** — if mediation on the routine path is procedural (Layer 1), the implementation MUST emit the amortized-small (not literal-zero) disclosure for no-delay/no-bandwidth. A literal-zero claim with a procedural routine-path check *fails* this group. + +**Group N (when — at construction):** + +- **Construction-order test** — confirm the perimeter is established *before* workspace contents are reachable: `ashe workspace init` (or equivalent) runs as step 1, and there is no window in which contents are accessible outside the exposed capability surface (composes with ADR-017 Commitment 1). +- **No-front-gate test** — confirm the boundary is the system's *shape*, not a gate in front of an already-built system: removing the ASHE component MUST make the guarded actions *unreachable* (structural), not *ungated-but-reachable* (bolted-on). A configuration where disabling ASHE restores direct access *fails* this group. + +**Group R (where — concentrated scope):** + +- **Path-classification test** — confirm the implementation classifies actions into the routine path (~98%, Tier A/B) and the Tier C boundary (~2%), and that the two are enforced *differently*: literal-zero/structural on the former, deliberate weight on the latter. +- **No-uniform-enforcement test** — confirm a routine Tier A action incurs no round-trip, no added token, and no human prompt, while a representative Tier C action (production deploy / secret access / escalation / irreversible destruction) does invoke the explicit boundary. An implementation that gates the 98% the same way as the 2% *fails* this group. +- **Friction-frequency test** — over a representative workload, explicit-prompt frequency on the routine path MUST stay below the defined threshold (shares the ADR-017 Commitment 2 frictionlessness measurement). + +**Grading and disclosure**: the suite reports, per invariant and per facet, whether the property holds as **literal-zero (structural, Layer 3/4)** or **amortized-small (procedural, Layer 1, disclosed)**, per ADR-015 evidence grades. The two already-floored invariants — no data alteration (ADR-007) and no interference (ADR-009/ADR-018) — are asserted at every layer and re-checked here as regression guards (byte-identity in Group H; degradation-never-fails as a host-liveness check). + +**Runnable suite**: the four groups are implemented as an executable conformance scaffold at [`conformance/`](../conformance/) — a language-neutral test manifest (`src/manifest.ts`, keyed by the test ids above) plus a TypeScript/vitest realization. An implementation plugs in by implementing the `AsheConformanceAdapter` contract (`src/adapter.ts`), whose four method blocks map one-to-one to the four facets; with no adapter configured every group skips. An illustrative correctly-applied adapter (`src/examples/structural-reference-adapter.ts`) makes the suite self-verifying. + +--- + +**ADR-020 makes "weightless" a conformance predicate, not an adjective. An implementation may claim the four hard invariants only under proper application: the object-capability primitive (what), applied structurally (how), at construction (when), on the dominant path (where) — all four, conjunctively. Proper application *removes* the work; it does not accelerate it. A fast check is still a check, still weight; weightless means there is no check at all on the path that matters.** diff --git a/decisions/INDEX.md b/decisions/INDEX.md index 505b177..65a4d26 100644 --- a/decisions/INDEX.md +++ b/decisions/INDEX.md @@ -39,6 +39,7 @@ Pattern: Michael Nygard's ADR template. | **017** | [Sealed workspace as foundational development pattern](ADR-017-sealed-workspace-foundational-dev-pattern.md) | Accepted | 2026-05-26 | Reference arch, protocol | | **018** | [`.well-known/ashe` web-side interaction-point convention](ADR-018-well-known-ashe-web-side-interaction-point.md) | Accepted | 2026-05-27 | Protocol, reference arch | | **019** | [Execution-class distinction — provider-call / agent-worker / occupant](ADR-019-execution-class-distinction.md) | **Proposed** (pending working-code validation via CONSTRUCT-CLAUDE-OCCUPANCY-DESIGN-v0) | 2026-05-27 | Protocol, reference arch | +| **020** | [Weightlessness is conformant only under proper application](ADR-020-weightlessness-proper-application-conformance.md) | Accepted | 2026-05-30 | Protocol, reference arch, paradigm | --- diff --git a/design/01-capability.md b/design/01-capability.md new file mode 100644 index 0000000..afb8303 --- /dev/null +++ b/design/01-capability.md @@ -0,0 +1,130 @@ +# Element 01: Capability + +| Field | Value | +|---|---| +| Status | ✅ running code | +| Layers | 1–4 (representation changes per layer; semantics constant) | +| ADRs | [003](../decisions/ADR-003-invariant-language.md) (invariant language), VISION §1 (object-capability lineage) | +| Reference code | [`conformance/src/protocol/capability.ts`](../conformance/src/protocol/capability.ts) | +| Depends on | nothing (root primitive) | +| Depended on by | 02 Lease, 03 Mediation, 08 Audit, 10 Wire, 11 Token — everything | + +A **capability** is an unforgeable, transferable reference that *is* the authority to perform a class of action. Holding the reference = having the authority; not holding it = the action is **unnameable**, not merely denied. This is the object-capability model (Dennis & Van Horn 1966; KeyKOS; E; Capsicum) at the root of ASHE's 50-year lineage (VISION §1). The single most important property: **authority cannot be amplified** — you can only hold, attenuate, or pass on what you were given. + +--- + +## 1. Technology + +The *semantics* are constant across layers; the *representation* hardens as enforcement deepens. + +| Layer | Representation of a capability | Unforgeability mechanism | +|---|---|---| +| **1 — cooperating SDK** | An in-process object reference (branded/opaque type); a `CapabilitySet` is a closure-private map | Language module privacy: the constructor mint-token is module-scoped, so a capability cannot be constructed from outside the issuing module (see reference code) | +| **2 — runtime hook** | Same object, but the runtime's standard library (`fs`, `net`, `child_process`) will only act when handed a live reference; raw calls are intercepted | Runtime mediation: the hooked stdlib refuses ambient (referenceless) operations | +| **3 — OS mediation** | An OS handle — a file descriptor, a Capsicum capability, a seccomp-bpf-gated syscall context, a gVisor/Firecracker capability | The kernel/sandbox: an fd you were never passed is not in your descriptor table; there is no namespace in which to express the call | +| **4 — hardware root** | A cryptographically signed token, sealed to a TPM/TEE, verified by hardware before the operation is admitted | Cryptography + hardware attestation: forging requires the sealing key, which never leaves the secure element | + +**Data structures (Layer 1 reference):** + +- `Capability` — `{ name: string, id: string }`, constructible only via the module-private mint token. +- `CapabilityIssuer` — the trust root; the sole minter. A deployment holds exactly one logical issuer chain (delegable). +- `CapabilitySet` — an immutable `Map`; the only derivation is `attenuate(allowedNames) → CapabilitySet` (subset-or-smaller). There is deliberately **no** `grant`/`union`. + +**Dependencies:** SHA-256 (only at Layer 4, for token signing); nothing else at Layers 1–3. + +--- + +## 2. Application + +A capability appears at every authority boundary in ASHE: + +- **Lease issuance (02)** packages a `CapabilitySet` as standing authority for a session. +- **Mediation (03)** asks one question — *does this actor hold a reference naming this action?* — and that question is the entire routine-path check. +- **Cascade attenuation (ADR-017)**: a parent agent spawning a sub-agent passes an *attenuated* set; the sub-agent structurally cannot exceed it. +- **Wire/token (10/11)**: a capability crossing a process or host boundary is serialized (Layer 1–2: by reference within a trust domain; Layer 4: as a signed token). + +Canonical use cases: *a JPEG-parser agent that never holds `secret.ssh-key.read` cannot name SSH keys at all* (VISION §1); *a CI runner holds `test.run` + `build.invoke` but not `deploy.production`* (ADR-017 role templates). + +--- + +## 3. Algorithm + +### 3.1 Minting (issuance of the primitive) +Only the issuer mints. Each mint produces a fresh, distinct reference even for the same name (so identity ≠ name — two holders of "code.read" hold *different* references, which matters for revocation and provenance). + +- **Invariant I1 (unforgeability):** a `Capability` exists ⟺ it was returned by `CapabilityIssuer.mint`. Enforced structurally (module-private token / OS handle table / signed token). +- **Complexity:** O(1) mint. + +### 3.2 Attenuation (the only derivation) +Given a held set `S` and a requested name set `R`, produce `S' = { c ∈ S : c.name ∈ R }`. + +- **Invariant I2 (no amplification):** `S' ⊆ S` always. Names in `R \ names(S)` are **silently dropped**, never created. This is what makes "a sub-actor exceeding its parent" *unconstructable* rather than *rejected* — there is no code path that adds authority. +- **Invariant I3 (monotone cascade):** attenuation composes — `attenuate(attenuate(S, R₁), R₂) = attenuate(S, R₁ ∩ R₂) ⊆ S`. A grandchild cannot re-acquire what a child dropped. +- **Complexity:** O(|S|). +- **Security property:** the maximal authority of any descendant in a spawn cascade is bounded above by the root grant, with no runtime check required to enforce the bound — the bound is structural. + +### 3.3 Holding / naming check +`has(name)` is a map lookup; `holds(cap)` is an identity comparison (`set[cap.name] === cap`). The distinction matters: `has` answers nameability (used by routine mediation); `holds` answers provenance (used by audit/revocation to distinguish *this* grant from a same-named one). + +- **Complexity:** O(1). + +### 3.4 Layer-4 verification (when representation is a signed token) +`verify(token) → bool`: check the signature against the issuer's public key and the TPM/TEE attestation chain; check `name`, `id`, not-expired, not-revoked. Only at Layer 4 is naming a *cryptographic* check rather than a *structural* absence; this is the one layer where the routine path pays a verification cost, justified by the hardware-rooted trust it buys. + +--- + +## 4. Pseudocode + +```text +# ── Minting (issuer is the sole trust root) ────────────────────────── +MINT ← module_private_unique_token # cannot be referenced externally + +class Capability(token, name, id): + if token ≠ MINT: raise "unforgeable: obtain from issuer" # Invariant I1 + self.name, self.id ← name, id + +class CapabilityIssuer: + seq ← 0 + func mint(name) -> Capability: + seq ← seq + 1 + return Capability(MINT, name, name + "#" + seq) # fresh identity each time + +# ── CapabilitySet: attenuate-only (no grant/union exists) ──────────── +class CapabilitySet(caps): + byName ← map{ c.name → c for c in caps } + + func has(name) -> bool: return name in byName + func holds(cap) -> bool: return byName.get(cap.name) is cap # identity, not name + func names() -> list: return keys(byName) + + func attenuate(allowed) -> CapabilitySet: # Invariant I2/I3 + kept ← [ byName[n] for n in byName.keys() if n in allowed ] # subset only + return CapabilitySet(kept) # dropped names are NOT minted + +# ── Layer-4 only: cryptographic naming ─────────────────────────────── +func verify_token(tok, issuer_pubkey, now) -> bool: + return attestation_chain_valid(tok) + and signature_valid(tok, issuer_pubkey) + and tok.expiresAt > now + and not revoked(tok.id) +``` + +--- + +## 5. Conformance + +Exercised directly by **ADR-020 Group W (what)**: +- `W1-unnameability` — an unheld capability yields *absence* (mediation returns `UNNAMEABLE`), never a `DENIED` evaluation. (Tests I1 indirectly: there is no reference to evaluate.) +- `W2-zero-ambient-authority` — `reachableAuthorities` equals exactly the held names. (Tests no ambient surface.) +- `W3-attenuation` — a sub-actor's set ⊆ parent's. (Tests I2/I3.) + +Unit-tested in [`tests/protocol/capability.test.ts`](../conformance/tests/protocol/capability.test.ts): external construction throws; distinct mints; attenuate-only; `contains` recognises subsets. + +--- + +## 6. Failure modes & limits + +- **Cannot prevent misuse *within* a held capability** (ADR-014 honest limit): if you hold `code.write`, ASHE does not judge whether a given write is wise — only that you may write. Granularity of the capability name is the only lever. +- **Identity collision is impossible by construction** (fresh `id` per mint) but **name collision is intentional** — two grants of `code.read` are distinct references with the same nameability; revocation targets `id`, nameability targets `name`. +- **Layer 1–2 unforgeability assumes an uncompromised runtime** (ADR-014 trust assumption): in-process native code that bypasses the module system can fabricate references. This is exactly why Layer 3/4 exist — at Layer 3 the fd table is the kernel's, not the process's, to forge. +- **Trust regress terminates at the issuer** (Layer 1–3) or **the hardware root** (Layer 4); ASHE does not pretend the regress is infinite (ADR-014). diff --git a/design/02-lease.md b/design/02-lease.md new file mode 100644 index 0000000..93a8504 --- /dev/null +++ b/design/02-lease.md @@ -0,0 +1,142 @@ +# Element 02: Lease + +| Field | Value | +|---|---| +| Status | ✅ issuance · 🟡 revocation (spec'd here, partial code) | +| Layers | 1–4 | +| ADRs | [017](../decisions/ADR-017-sealed-workspace-foundational-dev-pattern.md) (standing capabilities), [WEIGHTLESS.md](../WEIGHTLESS.md) (amortization), [009](../decisions/ADR-009-deployment-profiles.md) (degradation), [015](../decisions/ADR-015-validation-methodology-and-tiered-claims.md) (revocation-vs-speed trade) | +| Reference code | [`conformance/src/protocol/lease.ts`](../conformance/src/protocol/lease.ts) | +| Depends on | 01 Capability | +| Depended on by | 03 Mediation, 08 Audit, 14 Agent-side enforcement | + +A **lease** binds a `CapabilitySet` to a subject for a bounded window. It is **where weight is paid** (WEIGHTLESS.md): the cost — identity validation, policy evaluation, audit, the handshake — is paid *once*, at issuance, at the boundary. The steady-state per-action path then has nothing left to check but local possession. At `N` actions per lease, per-action overhead → `boundary_cost / N` → ~0. This is the TLS handshake-vs-symmetric-crypto split generalized from bytes to actions. + +The hard part of a lease is not issuance — it is **revocation**: the tension (named in [ADR-015](../decisions/ADR-015-validation-methodology-and-tiered-claims.md) and [WEIGHTLESS.md](../WEIGHTLESS.md)) between *fast local validation* (don't phone home per action) and *prompt revocation* (a withdrawn capability must stop working soon). This spec resolves it with **short TTL + epoch + revocation list at the boundary**. + +--- + +## 1. Technology + +| Layer | Lease representation | Revocation mechanism | +|---|---|---| +| **1 — SDK** | In-memory `Lease { subject, capabilities, issuedAt, expiresAt, epoch }` | Issuer holds a revocation set; checked at renewal (TTL boundary), not per action | +| **2 — runtime** | Same, plus the hooked stdlib consults the active lease | As Layer 1; renewal interval enforced by the runtime | +| **3 — OS** | Lease materialized as the process's capability/fd set at spawn; TTL = process/namespace lifetime or a refresh timer | Kernel revokes by closing fds / tearing the namespace; eBPF map update propagates an epoch bump | +| **4 — hardware** | Signed, TPM-sealed token with `expiresAt` + `epoch`; verified per sensitive op | Short TTL + signed revocation list (CRL/OCSP-style) verified at the boundary; hardware refuses expired/revoked epochs | + +**Data structures:** + +``` +Lease = { subject, capabilities: CapabilitySet, issuedAt, expiresAt, epoch } +LeaseAuthority = { now(), revoked: Set, epoch: int } +``` + +**Dependencies:** a clock source (monotonic preferred); at Layer 4, the signing key + a revocation-list distribution channel. + +--- + +## 2. Application + +- **Session establishment**: an agent authenticates (Element 04 Identity), declares intent (Element 07), and receives a lease — the single boundary event for the whole session. +- **Standing capabilities (ADR-017 C2)**: the lease *is* the frictionlessness mechanism — routine in-scope actions ride the lease with no per-action prompt. +- **Cascade (ADR-017)**: a sub-agent gets a *child lease* whose capability set is attenuated and whose TTL is ≤ parent's. +- **Graceful degradation (ADR-009)**: if the issuer is unreachable, an existing unexpired lease keeps working (fail-functional for already-granted authority); a *new* lease cannot be issued (fail-closed for new authority). + +--- + +## 3. Algorithm + +### 3.1 Issuance (the amortizable boundary event) +Given holder authority `H` (the principal's full set), a requested `scope` (names), and a TTL: + +``` +issue(subject, H, scope, ttl): + capabilities ← H.attenuate(scope) # Invariant: lease ⊆ holder (no amplification) + issuedAt ← now() + return Lease{ subject, capabilities, issuedAt, expiresAt: issuedAt+ttl, epoch: authority.epoch } +``` + +- **Invariant L1:** `lease.capabilities ⊆ holder` — a lease never carries authority the subject lacked. (Follows from attenuation, Element 01 I2.) +- **Cost accounting:** all expensive work (identity check, policy eval, audit-write) happens here, *once*. Amortized per-action cost = issuance_cost / N. +- **Complexity:** O(|scope|). + +### 3.2 Validation (the steady-state per-action check — must be cheap) +``` +active(lease, now): + return now ≥ lease.issuedAt + and now < lease.expiresAt + and lease.epoch ≥ authority.min_valid_epoch # epoch gate (see 3.3) +``` +- **Routine path:** at Layers 1–3 this is a comparison and a possession check — **no network callout** (the WEIGHTLESS no-bandwidth/no-delay target). At Layer 4 a signature/epoch check runs (the disclosed cost of hardware-rooted trust). +- **Complexity:** O(1). + +### 3.3 Revocation — resolving fast-validation vs prompt-revocation +The trade (ADR-015): you cannot have *both* zero-callout per-action validation *and* instant global revocation. ASHE chooses **bounded staleness** via three composing mechanisms: + +1. **Short TTL** — a lease self-expires in `ttl` (e.g., minutes for routine, seconds for Tier-C-adjacent). Worst-case staleness ≤ `ttl`. Renewal re-checks the revocation set at the boundary, off the hot path. +2. **Epoch bump** — to revoke *everything* fast (incident response), the authority increments `min_valid_epoch`; all leases with a lower epoch fail validation at their next check. Propagation cost is one integer pushed to validators (an eBPF map write at Layer 3; a CRL field at Layer 4). +3. **Targeted revocation list** — to revoke *one* capability, add its `id` to `revoked`; consulted at renewal and (for Tier-C) at the explicit boundary. + +``` +revoke_one(capId): authority.revoked.add(capId) # effective ≤ next renewal / at Tier-C boundary +revoke_all(): authority.min_valid_epoch ← authority.epoch + 1 # effective ≤ next validation +renew(lease, scope, ttl): + if any(c.id in authority.revoked for c in lease.capabilities): drop those c # at the boundary + return issue(lease.subject, current_holder_authority(lease.subject), scope, ttl) +``` + +- **Security property:** maximum exposure window after revocation = `min(ttl, time-to-next-validation)` for epoch revocation, and `ttl` for targeted revocation — **bounded, tunable, and disclosed**, never "instant" (which is unachievable without per-action callout) and never "never" (which is unsafe). +- **Tier-C exception:** high-stakes actions (Element 06) consult `revoked` *synchronously at the explicit boundary* — they already pay round-trip weight by design, so revocation there is immediate. The bounded-staleness window applies only to the routine path, where it belongs. + +--- + +## 4. Pseudocode + +```text +class LeaseAuthority: + now ← clock + revoked ← set() # capability ids + epoch ← 0 + min_valid_epoch ← 0 + + func issue(subject, holder: CapabilitySet, scope, ttl) -> Lease: # boundary cost paid here + t ← now() + return Lease{ subject, + capabilities: holder.attenuate(scope), # L1: ⊆ holder + issuedAt: t, expiresAt: t+ttl, epoch: self.epoch } + + func active(lease, t) -> bool: # hot path: O(1), no callout + return lease.issuedAt ≤ t < lease.expiresAt + and lease.epoch ≥ self.min_valid_epoch + + func revoke_one(capId): self.revoked.add(capId) # ≤ ttl staleness + func revoke_all(): self.min_valid_epoch ← self.epoch + 1 # ≤ next-validation staleness + + func renew(lease, scope, ttl) -> Lease: # off hot path + live_holder ← holder_authority(lease.subject) + .attenuate([n for n in names if id_of(n) not in self.revoked]) + return issue(lease.subject, live_holder, scope, ttl) + +# Tier-C synchronous revocation check (only the ~2% path pays this) +func mediate_tierC(actor, action, lease): + if not active(lease, now()): return UNNAMEABLE_OR_EXPIRED + if id_of(action.cap) in revoked: return DENIED # immediate at the boundary + return ALLOWED_WITH_EXPLICIT_BOUNDARY +``` + +--- + +## 5. Conformance + +- **ADR-020 Group H (how)** — `active()` on the routine path must add no boundary step at Layers 1–3 (structural), and a Layer-1 implementation must *disclose* the amortized-small grade. Revocation staleness is a **disclosed** property, not a hidden one. +- Unit-tested in [`tests/protocol/lease.test.ts`](../conformance/tests/protocol/lease.test.ts): a lease carries ≤ holder authority; `leaseActive` respects the window (issue inclusive, expiry exclusive). +- **To add (revocation tests):** epoch-bump invalidates lower-epoch leases at next validation; targeted revocation drops the capability at renewal; Tier-C synchronous revocation is immediate. + +--- + +## 6. Failure modes & limits + +- **No instant global revocation without per-action callout** — physics/CAP. ASHE's answer is *bounded, disclosed staleness*, tuned by TTL per tier. This is the honest WEIGHTLESS limit, not a bug. +- **Clock dependence** — TTL relies on a clock; skew widens or narrows the window. Mitigation: monotonic clocks for validation; NTP discipline; at Layer 4, hardware time. +- **Issuer unavailability** (ADR-009 degradation): existing leases keep working until expiry (fail-functional); new issuance fails closed. A long-TTL lease is more available but less promptly revocable — the tunable trade, made explicit at issuance. +- **Renewal storms** — many short-TTL leases renewing simultaneously load the issuer. Mitigation: jittered TTLs, renewal coalescing, per-subject renewal caching. diff --git a/design/03-mediation.md b/design/03-mediation.md new file mode 100644 index 0000000..4e5b04a --- /dev/null +++ b/design/03-mediation.md @@ -0,0 +1,119 @@ +# Element 03: Mediation + +| Field | Value | +|---|---| +| Status | ✅ running code | +| Layers | 1–4 (the *placement* of the interception point is the layer; the decision logic is constant) | +| ADRs | [007](../decisions/ADR-007-interception-chain-pattern.md) (interception chain), [008](../decisions/ADR-008-validation-graph-tiny-onnx.md)/[010](../decisions/ADR-010-standalone-graph-engine.md) (the decision engine), [014](../decisions/ADR-014-phased-enforcement-model.md) (where the point sits per layer), [020](../decisions/ADR-020-weightlessness-proper-application-conformance.md) (the structural-vs-procedural gate) | +| Reference code | [`conformance/src/protocol/mediation.ts`](../conformance/src/protocol/mediation.ts) | +| Depends on | 01 Capability, 02 Lease, 06 Tier | +| Depended on by | every enforced action; 08 Audit (records each decision) | + +**Mediation** is the interception point where an attempted action meets ASHE. Its defining property (ADR-020): on the routine path the decision was *already made* when the capability was granted, so mediation **passes through with no boundary step and the payload untouched**; the explicit boundary runs *only* at the ~2% Tier-C path where interference is the intended function. An action whose capability the actor does not hold is **`UNNAMEABLE`** — an absence, never a `DENIED` evaluation (which would betray that the action was nameable and a check ran). + +--- + +## 1. Technology + +Mediation is the *same decision* placed at progressively lower substrates (this is precisely the ADR-014 layer trajectory): + +| Layer | Where the interception point physically sits | What it can mediate | +|---|---|---| +| **1 — SDK** | In the ASHE library call path / cooperating IPC boundary ([ADR-007](../decisions/ADR-007-interception-chain-pattern.md) is the Layer-1 mechanism) | Cooperating calls; bypassed by raw OS calls | +| **2 — runtime** | Hooked stdlib (`fs.write`, `child_process.spawn`, `http.request` → ASHE) | All code in the runtime, adopted or not | +| **3 — OS** | Syscall mediation — eBPF/LSM (Linux), seccomp-bpf, gVisor/Firecracker sandbox boundary | Every syscall; bounds arbitrary in-process compromise | +| **4 — hardware** | Sensitive ops gated by hardware token verification | + protection against a compromised kernel for those ops | + +The decision logic that runs *at* the point is identical at every layer — a `Mediator` over a `TierRegistry`, optionally driving a **validation graph** (Elements 05/06) for non-trivial evaluators and writing to an **audit log** (Element 08). The interception-chain contract ([ADR-007](../decisions/ADR-007-interception-chain-pattern.md)) requires the point be **idempotent with no dispatch-state mutation** — it decides, it never rewrites (this is the WEIGHTLESS *no-data-alteration* floor). + +--- + +## 2. Application + +- **Routine path (~98%, Tier A/B):** the vast majority of agent actions — file reads/writes in scope, test runs, in-scope network fetches — pass through structurally. This is where weightlessness lives. +- **Tier-C boundary (~2%):** production deploy, secret access, capability escalation, irreversible destruction — the explicit gate runs (synchronous revocation check per Element 02 §3.3, possible human/multi-party approval, full audit). +- **Unnameable attempts:** an injected/compromised agent reaching for authority it was never leased gets *absence* — and the attempt is still audited (Element 08), turning prompt-injection probes into high-signal audit events. + +--- + +## 3. Algorithm + +### 3.1 The core decision (constant across layers) +``` +mediate(actor, action) -> (decision, boundaryInvoked, payloadDelivered): + tier ← TierRegistry.classify(action.capability) + + if not actor.canName(action.capability): # Element 01 has() + decision, boundaryInvoked ← UNNAMEABLE, false # absence, not a check + else if tier == C: + decision, boundaryInvoked ← evaluate_boundary(actor, action), true + else: + decision, boundaryInvoked ← ALLOWED, false # routine: structural pass-through + + audit?.append(record(actor, action, tier, decision, boundaryInvoked)) # local, off critical path + return (decision, boundaryInvoked, action.payload) # payload byte-identical (no-data-alteration) +``` + +- **Invariant M1 (no false denial on the routine path):** if the capability is held and tier ≠ C, the result is `ALLOWED` with `boundaryInvoked = false`. No evaluation runs. (ADR-020 Group H.) +- **Invariant M2 (absence, not denial):** unheld ⇒ `UNNAMEABLE`, never `DENIED`. `DENIED` is reserved for *held-but-revoked-at-the-Tier-C-boundary* (Element 02). (ADR-020 Group W.) +- **Invariant M3 (no mutation):** `payloadDelivered ≡ action.payload`, bytewise. (ADR-007 idempotence; WEIGHTLESS no-data-alteration.) +- **Complexity:** routine path O(1) (a classify + a possession check); Tier-C path = cost of `evaluate_boundary` (the only place a validation graph / approval / callout runs). + +### 3.2 The Tier-C boundary evaluator +`evaluate_boundary` is where the validation graph (Element 05) runs: lease still active? capability not revoked (synchronous, Element 02 §3.3)? intent reconciles (Element 07)? anomaly score below threshold (Element 06)? multi-party approval satisfied? Each is a node; the graph short-circuits on first deny. This is the *only* path that pays for these checks — concentrating weight where interference is the point (ADR-020 *where* facet). + +### 3.3 The interception-chain contract (ADR-007) +The point is one link in an ordered chain of interceptors (audit, policy, rate-limit, …). Contract: each interceptor is **idempotent**, **must not mutate dispatch state**, and either passes the action along or short-circuits with a decision. The chain is the extension model — new concerns (e.g., a compliance interceptor) are added as links without touching the core. + +--- + +## 4. Pseudocode + +```text +class Mediator: + tiers : TierRegistry + audit : AuditLog? # optional; local append + graph : ValidationGraph? # optional; only consulted at Tier C + + func mediate(actor, action) -> MediationResult: + tier ← tiers.classify(action.capability) + + if not actor.canName(action.capability): + res ← { ALLOWED?:no, decision: UNNAMEABLE, boundaryInvoked: false } # M2 + elif tier == C: + res ← { decision: self.evaluate_boundary(actor, action), + boundaryInvoked: true } # the 2% + else: + res ← { decision: ALLOWED, boundaryInvoked: false } # M1: structural + + res.payloadDelivered ← action.payload # M3: untouched + if self.audit: self.audit.append({ actor.id, action.capability, tier, + res.decision, res.boundaryInvoked, now() }) + return res + + func evaluate_boundary(actor, action) -> Decision: # only path that pays + if not lease_active(actor.lease, now()): return EXPIRED + if id_of(action.capability) in revoked: return DENIED # synchronous (Element 02) + if self.graph and not self.graph.eval(actor, action): return DENIED + if requires_approval(action) and not approval_satisfied(action): return DENIED + return ALLOWED +``` + +--- + +## 5. Conformance + +The element most directly tied to **ADR-020**, across three groups: +- **Group H (how)** — `H1-no-added-step` (routine path interposes nothing at a structural layer), `H2-byte-identity` (M3), `H3-layer-disclosure` (Layer-1 procedural mediation must disclose amortized-small). +- **Group W (what)** — `W1-unnameability` (M2: `UNNAMEABLE`, never `DENIED`). +- **Group R (where)** — `R2-no-uniform-enforcement` (routine incurs no round-trip/token/prompt; Tier-C does invoke the boundary). +- Unit-tested in [`tests/protocol/mediation.test.ts`](../conformance/tests/protocol/mediation.test.ts): routine allowed w/ no boundary + payload intact; Tier-C allowed w/ boundary invoked; unheld ⇒ `UNNAMEABLE` ≠ `DENIED`. + +--- + +## 6. Failure modes & limits + +- **Layer-1 mediation is bypassable by non-cooperating code** (ADR-014): raw OS calls skip the SDK point. Mitigation is *layer*, not logic — Layer 2 catches runtime code, Layer 3 catches syscalls. The decision logic is unchanged; only the point moves lower. +- **Cannot mediate what it cannot observe** (ADR-014 honest limit): in-memory state mutations with no syscall/stdlib touch are invisible at all layers — physics. +- **The Tier-C boundary is a genuine latency cost** — and is *supposed* to be: it is the deliberate-weight ~2% (ADR-020 *where*). Misclassifying a routine action as Tier-C is a weightlessness regression caught by Group R; misclassifying Tier-C as routine is a *security* regression caught by tier-classification review (Element 06). +- **Idempotence is a contract, not a guarantee against buggy interceptors** — a non-conformant interceptor that mutates dispatch state violates ADR-007; the conformance suite's byte-identity test (H2) is the detector. diff --git a/design/INDEX.md b/design/INDEX.md new file mode 100644 index 0000000..2ac613c --- /dev/null +++ b/design/INDEX.md @@ -0,0 +1,87 @@ +# ASHE — Element Design Specifications + +This directory drills every architectural element of ASHE down to the engineering level. Each element is defined across four facets: + +1. **Technology** — what actually implements it, *per enforcement layer* ([ADR-014](../decisions/ADR-014-phased-enforcement-model.md) Layer 1→4). The same element is realized differently as enforcement deepens. +2. **Application** — where it appears in the system, what depends on it, the use cases it serves. +3. **Algorithm** — the precise procedures, the invariants they preserve, complexity, and the security properties they buy. +4. **Pseudocode** — concrete, implementable pseudocode, kept consistent with the running reference primitives in [`conformance/src/protocol/`](../conformance/src/protocol/). + +These specs are the engineering expansion of the corpus. They are normative where they restate an ADR commitment and *informative* (reference-implementation guidance) where they describe one concrete realization. Where running code already exists, the spec is grounded in it and the doc says so. + +--- + +## Element catalog + +Grouped by subsystem. **Status**: ✅ running code in `conformance/src/protocol/` · 🟡 partial · ⬜ spec-only so far. + +### A. Core trust primitives +| # | Element | ADRs | Status | +|---|---|---|---| +| [01](01-capability.md) | **Capability** — the unforgeable reference; `CapabilitySet`; attenuation | [003](../decisions/ADR-003-invariant-language.md), VISION §1 | ✅ | +| [02](02-lease.md) | **Lease** — boundary-amortized authority; issuance / validation / **revocation** | [017](../decisions/ADR-017-sealed-workspace-foundational-dev-pattern.md), WEIGHTLESS | ✅ (issue) 🟡 (revoke) | +| [03](03-mediation.md) | **Mediation** — the interception chain; routine-path pass-through vs Tier-C boundary | [007](../decisions/ADR-007-interception-chain-pattern.md) | ✅ | +| 04 | **Identity & Principal** — OIDC / DID-compatible claims; actor binding | [002](../decisions/ADR-002-oidc-identity.md) | ⬜ | + +### B. Decision subsystem +| # | Element | ADRs | Status | +|---|---|---|---| +| 05 | **Validation graph & evaluators** — default-to-tiny-ONNX; standalone engine | [008](../decisions/ADR-008-validation-graph-tiny-onnx.md), [010](../decisions/ADR-010-standalone-graph-engine.md) | ⬜ | +| 06 | **Tier classification & risk scoring** — A/B routine, C boundary; anomaly score | [017](../decisions/ADR-017-sealed-workspace-foundational-dev-pattern.md) | ✅ (static) 🟡 (scored) | +| 07 | **Intent declaration & reconciliation** — declare-once, auto-validate | VISION §6, [017](../decisions/ADR-017-sealed-workspace-foundational-dev-pattern.md) | ✅ | + +### C. Provenance +| # | Element | ADRs | Status | +|---|---|---|---| +| 08 | **Audit log** — tamper-evident hash chain; append-only | [013](../decisions/ADR-013-multi-service-architecture.md) | ✅ | +| 09 | **Provenance & capability-grounded content** — per-action provenance | ADR-016 (forthcoming) | ⬜ | + +### D. Wire & tokens +| # | Element | ADRs | Status | +|---|---|---|---| +| 10 | **Wire format** — Protobuf canonical + JSON / TOON projections | [012](../decisions/ADR-012-wire-format-grpc-protobuf-with-projections.md), [006](../decisions/ADR-006-toon-dual-projection.md) | ⬜ | +| 11 | **Capability token** — serialized, signed, hardware-verifiable (Layer 4) | [012](../decisions/ADR-012-wire-format-grpc-protobuf-with-projections.md), [014](../decisions/ADR-014-phased-enforcement-model.md) | ⬜ | + +### E. Tri-surface +| # | Element | ADRs | Status | +|---|---|---|---| +| 12 | **Sealed workspace** (dev-side) — `ashe workspace init`; wall-up-first | [017](../decisions/ADR-017-sealed-workspace-foundational-dev-pattern.md) | ⬜ | +| 13 | **`.well-known/ashe`** (web-side) — discovery + handshake | [018](../decisions/ADR-018-well-known-ashe-web-side-interaction-point.md) | ⬜ | +| 14 | **Agent-side enforcement** — the phased-layer mechanism | [014](../decisions/ADR-014-phased-enforcement-model.md) | 🟡 | + +### F. Enforcement substrate (cross-cutting technology) +| # | Element | ADRs | Status | +|---|---|---|---| +| 15 | **Layer 1 — cooperating SDK** | [014](../decisions/ADR-014-phased-enforcement-model.md) | ✅ | +| 16 | **Layer 2 — runtime hook** (Node `fs`/`child_process`/`http`) | [014](../decisions/ADR-014-phased-enforcement-model.md) | ⬜ | +| 17 | **Layer 3 — OS mediation** (eBPF / gVisor / Firecracker) | [014](../decisions/ADR-014-phased-enforcement-model.md) | ⬜ | +| 18 | **Layer 4 — hardware root** (TPM / TEE attestation) | [014](../decisions/ADR-014-phased-enforcement-model.md) | ⬜ | + +### G. Services & deployment +| # | Element | ADRs | Status | +|---|---|---|---| +| 19 | **Multi-service architecture** — Session / Blueprint / Operator / Build / Audit | [013](../decisions/ADR-013-multi-service-architecture.md) | ⬜ | +| 20 | **Deployment profiles** — ASHE-core → ASHE-full; graceful degradation | [009](../decisions/ADR-009-deployment-profiles.md) | ⬜ | + +--- + +## Drill order (why this sequence) + +The trust spine first — **01 Capability → 02 Lease → 03 Mediation** — because every other element depends on them: there is no audit without something to audit, no wire format without a capability to serialize, no enforcement layer without a decision to enforce. These three also have running, tested code, so the specs are grounded rather than speculative. From there: the decision subsystem (05–07), provenance (08–09), wire/tokens (10–11), the tri-surface (12–14), and the enforcement substrate (15–18), finishing with services/deployment (19–20). + +## The four-facet template + +```markdown +# Element NN: + +| Field | Value | +| Status | ✅/🟡/⬜ | Layers | which of 1–4 it applies to | +| ADRs | … | Reference code | path if it exists | + +## 1. Technology — substrate per layer; data structures; dependencies +## 2. Application — consumers; use cases; where it sits in a request +## 3. Algorithm — procedures; invariants; complexity; security properties +## 4. Pseudocode — concrete, implementable +## 5. Conformance — which ADR-020 group / suite test exercises it +## 6. Failure modes — what it cannot do; degradation behavior +```