00 — Overview
What YayLayer is
AI agents write code fast; the bottleneck is no longer producing it but trusting it. "A human clicked approve" is a weak claim — approve what, exactly? YayLayer sits between production and trust, on one discipline: humans authorize intent, machines demonstrate conformance, and neither actor grades its own work.
Code is organized into Cells — a unit of code with a specification block above it. A human signs the spec + a plain-English Brief with a key on their phone; the signature covers the intent, never the code bytes. A deterministic, non-AI verifier re-derives whether the code still satisfies the signed spec and assigns a state. A CI gate blocks anything not-Green from main. For autonomous work, a human issues a bounded grant (Autopilot); the AI produces within limits and a human ratifies afterward. Every step is an immutable, attributable record.
The triad
Three actors, three kinds of claim, so no actor grades itself:
| Actor | Role | May assert |
|---|---|---|
| Human | Authority / judgment | "I authorize this intent/scope" · "I ratify this result." |
| Agent (AI) | Production — least trusted | "I created these Briefs, specs, and code." Never "…and it's correct." |
| Machine verifier | Deterministic assessment | "Under verifier X + ruleset Y, this exact code satisfies this exact spec → Z." |
Every statement material to an approval remains reconstructable forever unless an explicit retention policy says otherwise — and no historical record is ever silently rewritten.
That turns the question from "did a human approve this code?" into what an auditor actually needs: who had authority, where it came from, its limits, what the agent did, what the verifier established (and under which semantics), when a human ratified, and what changed afterward.
What it is not: not a code-approval system (a refactor preserving the spec needs no new signature); not a blockchain (just an append-only hash chain); not an AI grading itself (the verifier is deliberately non-AI — bounded, reproducible, versioned).
01 — Concepts
The vocabulary
Cell
The atomic unit YayLayer governs: a block of code plus a spec, delimited inline in the source. The spec lives with the code, so they never drift apart in version control.
//∷YAY⟨C-050⟩
// unit: memoKey
// intent: Build a stable cache key by joining arguments with a separator.
// in: parts: Array<string|number>
// out: key: string
// pure: yes
//∷YAY-END⟨C-050⟩
function memoKey(){ return Array.prototype.slice.call(arguments).join('|'); }
Every Cell has a stable id and a specHash = sha256(normalized spec block) — what the seal signs.
Brief
The human-readable headline over a change-set: a title + one to three sentences of intent, plus the Cells it covers. Signed with the specs, so it's the attributed record of what was commissioned. A Brief attributes intent — it never earns Green (only Cells do).
The two chains
The spine of the design — two authorities, kept strictly separate:
HUMAN AUTHORIZATION CHAIN IMPLEMENTATION VERIFICATION CHAIN
Brief + exact Cell specs authorized spec-set + exact code snapshot
→ human signature → verifier evidence + status
Because the human never signs code bytes, a refactor (const r = x + y → return x + y) needs no new signature if it still verifies. That keeps YayLayer approving intent, not implementations.
The human's three acts
| Act | What's signed | Code exists? |
|---|---|---|
| Forward spec-sign (default) | Brief + spec-set | No — intent before implementation |
| Grant (Autopilot) | a capability envelope | n/a — authorizes future production |
| Ratification | operation + Brief + spec-set + observed code + verification | Yes — review the result, then sign |
Forward-signing is the trunk; grant and ratification are the agentic branches. All three are the human side of the triad (phone key) — they differ only in what object is signed and whether code exists yet.
The three cryptographic identities
Not "everything gets signed" — three distinct facts:
- Artifact hashes — what exact thing? (specHash, code-tree, evidence).
- Verifier attestation — what did the machine conclude? Signed by the verifier's own key.
- Human signature — what did the human authorize? Signed by the phone key.
The two axes
Authority and verification are orthogonal — never collapse them into one traffic light:
AUTHORITY (human) VERIFICATION (machine)
Unapproved Red
Delegated Yellow
Ratified Green
So a Cell can be Delegated + Green (verifier happy, no human yet) or Ratified + Red (human approved the intent, but the code no longer verifies). The UI shows one calm state in the common case and surfaces both only when they diverge.
"Green is an event," not a property
Code isn't inherently Green — it's Green relative to a spec + verifier + semantics + evidence + time: GREEN(artifact, spec, verifier_semantics). A better verifier in 2029 doesn't make a 2026 Green fraudulent; it makes the claim more precise and can append a new assessment without rewriting the old.
States
| State | Meaning |
|---|---|
| Green | Code satisfies the signed spec (machine-proven where the language supports it). |
| Yellow | Signed but not fully proven — mutation-weak, inert, needs re-sign, or an honest cap. |
| Red | Code contradicts the spec, or a spec-derived test fails. |
| Unsigned | No valid human signature covers the current spec. |
| Pink | Code with no Cell governing it — un-specced units, or loose top-level code. Blocks the gate. |
02 — Spec reference
Writing a Cell spec
A Cell is a spec block in comments, immediately above the unit it governs. This is the exact syntax and the fields the verifier reads.
Markers
A spec block is delimited by two markers carrying the same Cell id:
//∷YAY⟨C-050⟩
// unit: memoKey
// intent: Build a stable cache key by joining arguments with a separator.
// in: parts: Array<string|number>
// out: key: string
// ensures: joins parts with "|" in order; [] → ""
// pure: yes
//∷YAY-END⟨C-050⟩
function memoKey(...parts){ return parts.join('|'); }
The markers are comment-agnostic: the parser strips any leading comment punctuation (//, #, --, ;, %, !, ', (*, <!--, *) and trailing block closers, so the same block works in essentially any language. Inside the block, every line is a key: value pair; a line with no key: continues the previous field. The id is any of [A-Za-z0-9._-]. A begin with no matching end is a reported problem, and its code falls to Pink — malformed markers fail safe, never silently (see Security).
Fields
Any key: value is preserved, but these are the ones the verifier interprets. Provable marks a field the behavioural prover / effect net acts on; the rest are human-readable and drive Briefs, tags and policy.
| Field | Meaning | Role |
|---|---|---|
unit | Name of the code unit this Cell governs — must match the declaration below the block. Binds spec ⇔ code. | Required |
intent | One plain-English sentence: what it is for. Anchors the Brief. | Required |
in / out | Input parameters / return shape, in prose or type notation. | Describes |
ensures | A checkable postcondition/property. The behavioural prover executes the unit against this with spec-derived inputs — a strong ensures is what earns Green. Prose-only (no ensures) caps at Yellow. | Provable |
pure | yes/no. A pure: yes Cell that actually touches network, filesystem or DB goes Red — enforced by the language's effect net. | Provable |
effects | Declared side effects for a non-pure unit (what it is allowed to touch). | Describes |
throws | Declared error conditions. Doubles as a signed exemption for guard code the inertness check would otherwise flag. | Provable |
perf | A performance/optimisation rationale. Signed exemption for perf-motivated branches under the inertness check. | Describes |
tag / tags | Classification words (comma/space separated). Drive Brief tagging and policy matching. | Governs |
sensitive | yes adds a synthetic sensitive tag — a cooperative hint policy rules can match (the authoritative control is owner-signed policy, not this field). | Governs |
risk | low/medium/high (default medium). A declared governance hint for Autopilot's --max-risk ceiling — high for money/auth/secrets/deploy/CI/irreversible, low for cosmetic or pure helpers. Cooperative (AI-writable); it never overrides the security guard or owner-signed policy. | Governs |
Vague prose never earns Green. Green requires a machine-checkable claim — an ensures the prover can execute (in a proven-tier language) or, at minimum, effect/purity claims the static net can check. See Languages for which tier each language reaches.
03 — Provenance architecture
The durable-audit heart
Every statement material to an approval remains reconstructable forever unless an explicit retention policy says otherwise — and nothing is ever silently rewritten.
YayLayer offers this at two levels — a per-project choice made at setup (full comparison under Standard vs Durable below). In short: Standard (default) archives the specs and attestations and references the code from git; Durable also archives the code (encrypted) in the project's own store, so the record survives even if git is lost or rewritten. They differ only in how code is stored — so when the text below says "in both Standard and Durable," that's what it means.
An approval is a chain of authority and evidence, every node an immutable, content-addressed object:
Human Grant G42
└─ Delegated operation A81 (or a direct forward sign)
├─ Brief B17
├─ Spec Set S91 (exact Cell revisions)
└─ Code Tree T93
└─ Verification V22 (verifier 1.8.4, ruleset 3, evidence E72 → GREEN)
└─ Human Ratification R12 (references V22's hash)
Preserve every historical spec — always
Specs are tiny and irreplaceable, so every signed Cell spec is archived in both Standard and Durable modes, content-addressed by its specHash. This kills the "history comes back blank" problem: the "as signed" spec never depends on git being intact. Integrity is free — the stored body must hash to the signed specHash.
Two capture points
The two chains have two birthdays: human authorization mints at sign (code doesn't exist yet); verifier attestation mints at commit / verify-pass (code exists then). Ratification is the exception — a human signs after code exists.
Verifier attestation
yay verify is ephemeral; yay attest makes "Green" a durable, signed fact, recording spec-set / code-tree / ruleset / evidence hashes / result / environment / timestamp — then hashing and signing it with the verifier's own key (machine-held, gitignored; the public verifier of record is pinned in config.verifier, and the key is never shipped in the package). A hash proves "unchanged"; a signature proves "the trusted verifier produced this," so a malicious agent can't fabricate a Green. The human seal references the attestation hash. Append-only, chained ledger (.yaylayer/attest.json); yay attest verify re-checks every one.
Verifier versioning
The verifier is a versioned participant. Releases are classified by capability — PATCH (semantics unchanged) · MINOR (new reasoning) · MAJOR (meaning of Green changed). The version is derived + self-asserting: a fingerprint over the live provers/effect-nets/checks is registered per version, so a changed detector that forgot its version bump is caught (yay capability; yay attest refuses on drift) — an attestation can never claim more than the verifier actually does. A stronger verifier can re-verify history and append new assessments (yay reverify --all): 2026 → GREEN under 1.0.0 and 2029 → YELLOW under 4.2.0 both stay true. Never a silent rewrite, never an automatic CI failure — historical status is separate from current policy, and the reverification posture (off / guarded / strict, optionally scoped to crown-jewel Cells) is how an org configures grandfathering.
Storage — the hybrid model
Invent as little as possible:
- Small YayLayer-native objects (specs, attestations, evidence, grants/ratifications) → a tiny content-addressed
.yaylayer/objects/store (sha256, deduped). The must-never-blank audit core. - Bulk source (Durable mode) → an encrypted, sha256-anchored archive (
.yaylayer/archive/, AES-256-GCM). A git-bundle shared incremental pack is an optional bulk-transport optimization on top; the guarantees hold either way.
The invariant that keeps any container honest: our sha256 over the archived plaintext is the trust anchor (never git's SHA-1), and dedup is by content hash (never a bundle-per-approval).
Standard vs Durable
A per-project setting chosen at yay init / yay adopt:
| Standard (default) | Durable | |
|---|---|---|
| Specs + attestations | Archived (always) | Archived (always) |
| Brief tree / spec history | Full | Full |
| Code at each point | By git reference (labeled if lost) | Archived (encrypted) — survives git loss |
| Best for | Normal projects | Regulated / long-lived audit |
Governance — designed in, not bolted on
The real cost of "keep code forever" isn't disk — it's governance. Durable mode ships with it:
- Audit metadata (hashes, attestations, signatures, tombstones) — kept in the clear and signed, so the chain is auditable without decrypting source.
- Source blobs — encrypted AES-256-GCM at rest and in transit, under a project-held key YayLayer never sees. The store holds ciphertext + the sha256 of the plaintext. Decryption is transient, in memory, only when an authorized human opens the "as signed" view.
- Owner-signed retention policy and a pre-archive secret scan (a secret archived forever is a forever-liability).
- Deletion tombstones — honest erasure without rewriting history:
Cell C-040 · revision C17
status: deliberately removed
hash: sha256:8d621b0f…
removed: 2026-09-14 · reason: "GDPR erasure request #4471"
You lose reconstructability of that one object; you keep the chain, the signatures (which cover the hash), and an auditable record that a deletion happened.
The integrity witness
Because YayLayer independently retains spec/tree hashes, evidence, and signatures, a rewritten git history can be compared against the ledger: "the code git now claims existed here isn't what YayLayer originally verified." At that point YayLayer isn't documenting the workflow — it's an independent integrity witness for it.
04 — Autopilot
Delegated execution
Autopilot is the friendly name; Delegated execution the formal one — bounded, revocable, ratified delegation with the human in control.
A grant is authorization to act. It is not approval of the resulting artifact. Reserve "approved" for actual human signatures.
Human ── signs ──▶ Grant G42 ("what the agent may do")
│ authorizes
▼
Agent operation A81 (Brief · Cells/specs · code · verification evidence)
│
▼
Awaiting ratification ── human reviews ──▶ Ratified (or Rejected)
State vocabulary
Never "auto-approved." On the authority axis: Delegated → Awaiting ratification → Ratified / Rejected / Superseded.
The grant is a capability envelope
A grant is a signed envelope — not just --for / --count / --cell but allowed/denied paths, max-risk, and whether child grants are permitted:
Grant G42
allowed: src/ui/**, src/utils/**
prohibited: authentication, secrets, CI config, signing config, migrations
dependencies: no additions
max cells: 8 max risk: medium
deployment: prohibited child grants: prohibited
A constraint is meaningful only if the verifier can detect a violation in the output — enforcement is by detection at verify/ratify time, not by sandboxing the agent. Scope constraints (paths, cells) are enforceable now; content constraints (deps, network, effects) arrive as verifier capability bumps. Never promise a constraint you can't verify — an unenforceable boundary is theater. And the agent must never be able to broaden its own grant: the authorization boundary lives outside the delegated authority.
Max-risk — the sensitivity ceiling
Risk is a level a Cell carries — low, medium or high — declared in its spec (risk: high) and defaulting to medium. A grant's --max-risk is a ceiling: the agent may auto-approve a Cell only if its risk is at or below the ceiling; a Cell above it falls outside the envelope, so a delegated change there is a gate-blocking grant violation and waits for a real human signature. So --max-risk medium lets Autopilot handle low/medium Cells but hands anything marked high back to you. It's a tunable dial the agent still can't exceed — weaker than non-delegable areas (an absolute owner-signed backstop no risk setting can open), stronger than nothing.
Child grants — bounded sub-agents & swarms
The child grants field decides whether the delegated agent may issue a sub-grant — a subset of its own envelope handed to a helper sub-agent, with no new human signature. It's the fan-out mechanism, and it scales to a whole swarm: an orchestrator over src/** spawns a refactor-helper scoped to src/ui/**, a test-helper to test/**, a docs-helper, … all working in parallel, all chaining back to the one human root grant — and every result still lands in the ratification queue. Off by default (highest-risk lever). Safe only when strictly attenuating: a child can only narrow scope/expiry/count/risk, can't re-enable non-delegable categories, is cryptographically chained to the parent (verify validates back to the human root and refuses any child that exceeds its parent), and is depth-bounded. So a swarm fans out the work, never the authority — the total power of the whole tree can never exceed the single grant you signed; the human root stays the sole source of authority, and children only divide it thinner.
Non-delegable by default — the security guard
"Autopilot" ≠ freedom everywhere. Every grant carries a security guard that is ON by default: the verifier refuses to auto-approve Cells in high-stakes areas — auth, payments, secrets, deploy, CI/infra — matched on the Cell's file path or its tags, with no per-project setup. Such a Cell needs a real human signature; a delegated approval over it is a gate-blocking violation. Lift the guard for a specific grant with --no-guard (a deliberate, loudly-warned choice). The guard's patterns live in the trusted verifier, so it improves for existing grants; the signed envelope records only the on/off intent.
Two deeper layers back it: owner-signed policy ({ "delegable": false } by path/tag/module) is the authoritative backstop that lives outside the AI-writable surface (so it holds even if a spec marker is stripped), and YayLayer's own trust config (roster, verifier, gate) is protected structurally — the AI can't enroll itself, edit the verifier, or widen its own grant. And you can scope any grant exactly: --allow/--deny paths, --allow-tag/--deny-tag, --cell, and --max-risk.
Ratification
The human catching up on authorization that never happened forward — a real intent-authorization done retrospectively with the code + evidence present. Because the code already exists, humans tend to rubber-stamp, so yay ratify must be meaningful review: what was authorized (grant scope), what the agent did (Brief, Cells, diff, effects, deps, perms), a boundary/deviation report, verification, then Ratify / Reject / Inspect.
The ratification object keeps the facts distinct: I approve this intent/spec and this is the implementation snapshot that existed when I decided — not these bytes may never change. A later refactor can stay under the same ratified spec if it re-verifies.
Ratification's whole purpose is a human vouching for the exact thing they reviewed. So yay ratify --sign must sign the exact reviewed bundle: compute a reviewed-bundle hash over grant + brief + spec-set + code-tree + evidence; at sign, recompute and refuse if anything changed — "implementation changed since review (reviewed T93, current T94) — run yay ratify again." Otherwise a human reviews T93 and unknowingly signs T94, turning ratification into a false record.
Rejected ratifications are valuable history
Don't erase a rejection — it records where agent autonomy failed human judgment. Persisting REJECTED events (with category + reason) is the substrate for earned autonomy: over time, "refactors — 97% ratified unchanged; dependency changes — 31% rejected → exclude from grants by default." The agent earns broad delegation where it's reliably ratified and loses it where humans keep rejecting.
05 — Languages
Three tiers of support
Which tier a language reaches is part of the verifier's capability version — so a language moving from static to proven is a capability bump that can re-verify history.
The Ceiling column is the best state a well-formed, signed Cell can reach at that tier — not the only state. Pink (a unit with no Cell) and Red (code that contradicts its spec) are outcomes for problem code and happen at every tier — they're failures, not ceilings. The tiers differ only in how high good code climbs: Green (proven) vs Yellow (checked but not executed).
| Tier | What the verifier does | Ceiling (good, signed Cell) |
|---|---|---|
| Proven | Cells · Pink for un-specced code · effect/purity nets · coverage · rename detection · executes against ensures (behavioral prover) | Green |
| Static | Same checks, but no execution | Yellow |
| Scanned | Marker-parsed only (no effect net) | Yellow (weaker) |
Support matrix
| Language | Support | Notes |
|---|---|---|
| JavaScript / TypeScript (JSX/TSX) | Proven | AST via Babel; behavioral prover + mutation + inertness; JSX render prover. Node = this. |
| Python | Proven | python3 subprocess; pure functions with ensures reach Green. |
| Ruby | Proven | ruby subprocess (Python pattern) + Ruby effect net. Proof covers top-level/module functions only; class/instance methods & framework-coupled code (Rails) → Yellow. |
| PHP | Proven | php subprocess (Python pattern) + PHP effect net. Same top-level-only proof scope; class/instance methods → Yellow. |
| Solidity | Static | Effect net today; behavioural proof needs an EVM harness (not yet available). |
| Rust | Static | Effect net today; behavioural proof needs a compile-harness (not yet available). |
| C# | Static | Effect net today; behavioural proof needs a compile-harness (not yet available). |
Proving limits (Ruby/PHP) & Rails: the subprocess provers currently resolve only top-level / module functions, so class/instance methods and framework-coupled code (ActiveRecord, params, Rails constants) skip to Yellow — honestly. So Rails can adopt YayLayer now for governance + the gate + provenance across the app; machine-proof lands on the pure, top-level slice (e.g. lib/ helpers), and the effect net still turns a pure: yes method that hits the DB/IO Red anywhere. Class/instance-method proving (→ pure Rails service objects / value objects) is the top language follow-up. .ex/.exs (Elixir) are scanned only.
Graceful degradation: behavioral proof detects the toolchain — present → prove; absent → honest Yellow, never an error. Determinism: the prover has no RNG (enumerated inputs, fixed mutants, synchronous) — same machine ⇒ identical verdict. The only nondeterminism is wall-clock timeouts + cross-environment drift, so an attestation records the environment and claims "Green under this verifier + environment at this time."
06 — Install & signing
Getting set up
Requirements: Node 18+ and git. Install git first — git is where the code proof lives.
npm install -g yay-layer
git init # first, so YayLayer's gitignore protects secrets before anything is staged
yay init # guided: signing mode → adopt? → tag set → Constitution → project AI → foundation seal
Choosing a signing mode
Decided at yay init. The human's private key lives in one place; the AI's machine only ever holds the public key.
| Mode | Where the key lives | Network | Third party | Best for |
|---|---|---|---|---|
| Local | This machine, passphrase-encrypted | Offline | None | Solo, quick starts, CI |
| Mobile · LAN | Your phone | Same Wi-Fi | None | Maximum privacy |
| Mobile · Relay | Your phone | Anywhere | Relay (E2E, blind) | Off-LAN / restrictive Wi-Fi |
Mobile modes pair once (scan a QR, create the key, save the 24-word recovery phrase, set a PIN, confirm a 6-digit code); later requests reach the same phone automatically. For LAN, brew install mkcert && mkcert -install gives a trusted cert so the phone shows no HTTPS warning. Relay travels through end-to-end-encrypted relay.yaylayer.com — it shuttles ciphertext only and can neither read code nor forge a signature.
Teaching your AI the rules (the Constitution)
yay constitution --for <harness> writes YayLayer's operating rules into your AI's instruction files, so a fresh session is spec-first from the first file. Harnesses: claude, agents, copilot, cursor, windsurf, cline, gemini, generic — combine with commas, or --for all.
yay constitution --for claude,cursor # CLAUDE.md + .cursorrules
After that you just talk to your AI: it drafts the spec and Brief, runs yay sign for you to approve on your phone, then builds and verifies.
Standard or Durable
Also chosen at yay init (--durable). Standard (default) archives specs + attestations and references code from git; Durable also archives the code encrypted in the project's own store, so the record survives even if git is lost. See Provenance.
07 — How-to
Recipes
Install git first, then yay-layer — git is where the proof lives.
Set up / adopt
yay init # files → signing key → adopt → tags → Constitution → project AI → foundation seal
yay adopt # scaffold specs over an existing codebase
yay adopt scaffolds a draft spec over every named unit (function / method). It deliberately does not rewrite code, so loose module-level code (bare statements that run at import) is reported, not auto-wrapped — it stays Pink and adopt points you to it. Your AI then wraps each such region into a spec'd unit (Constitution Art. 6: an IIFE around a spec'd function in JS/TS, or an entry point in Python), preserving order and scope. That split is intentional: the mechanical part is automated; the one part that changes runtime behaviour is left to the actor that understands the code. Existing Cells are never touched, renumbered, or re-signed — adopt only adds over code that has no Cell yet.
The normal loop (forward-signing)
Spec first → yay sign --title "…" --brief "…" → implement to match → yay verify → on Green, commit code + .yaylayer/ together (don't push unless asked).
Sign at your own pace (batch)
yay batch 8 # raise the barrier
yay batch off # a Brief per change
Autopilot & ratify
yay grant --for 2h --count 20 # bounded delegated authority (sign once on phone)
yay grant list yay grant revoke [id]
yay ratify # list delegated Cells (read-only)
yay ratify --sign # review the reviewed bundle, then human-sign
Don't like it? Don't ratify — Send back with a note; the AI revises and presents a forward Brief you sign, superseding the delegated version.
Verify & gate
yay verify yay verify --strict # CI: non-zero on Red/Unsigned/Pink
yay gate # write the CI workflow protecting main
yay gate writes the CI pipeline (runs yay verify --strict on every PR and pins your trust root) and prints the one-time branch-protection steps for your host. It is not GitHub-only — pick your platform with --for:
yay gate --for … | Writes | Then protect main via |
|---|---|---|
github (default) | .github/workflows/yaylayer.yml | Settings → Rules → Rulesets → require the gate check + PR + block force-pushes |
azure | azure-pipelines.yml | Repos → Branches → main → Branch policies → Build Validation (required) + reviewers |
gitlab | .gitlab-ci.yml | Protected branch + "Pipelines must succeed" + MR approvals |
bitbucket | bitbucket-pipelines.yml | Branch restrictions on main: require a passing build + PR + approvals |
gitea | .gitea/workflows/yaylayer.yml | Branch protection → require the gate status check + PR |
gerrit | .zuul.yaml + playbooks/yaylayer-gate.yaml | Gates via the Verified label — make Verified+1 a submit requirement (CI = Zuul, or Jenkins via the Gerrit Trigger plugin) |
Gerrit is the one that works differently: it has no "required check" — it gates on the Verified label. Your CI (Zuul natively, or Jenkins via the Gerrit Trigger plugin) runs yay verify --strict on every change (refs/for/*) and votes Verified ±1; you make Verified+1 a submit requirement so nothing merges until the gate is green. The command it runs is identical — only the vote-and-submit wiring is Gerrit-specific.
The contract is identical everywhere — run yay verify --strict on a protected branch that blocks merge on failure, with the trust root pinned; only the pipeline syntax and the settings page differ. The core (signing, verify, roster, seals, foundation, provenance) is pure git, so it works on any host or none. Branch protection lives in your host's settings only you control, so a leaked token or compromised dev machine can't switch it off — that's what makes the CI gate the real enforcement point, not the local hook. yay gate --hook also installs a local pre-push check for solo/offline feedback.
Teams: Cell numbering & merges
Several people, each in their own clone, building in parallel and merging — this is where a naïve sequential counter would break, because two branches would independently mint C-042 for two different Cells. YayLayer avoids that at the source.
Every id is C-<shard>-<n>. The <shard> is a short token unique to your working copy — it lives in gitignored .yaylayer/local.json and is never committed, so every clone has its own. Two contributors therefore mint ids in disjoint namespaces (C-3f2a-1 vs C-9b7c-1) that can never collide on merge. See yours anytime:
yay id # this clone's shard + the next id it would mint
Numbers are monotonic and never reused. A new Cell counts up from the highest C-<shard>-* ever seen (current tree and ledger history), so deleting a Cell retires its number forever — an id names one Cell for the life of the project, like a git commit hash. Deletions leave permanent gaps; that is correct for an audit trail, not a defect.
In a large, long-lived repo the ids stay readable and tell you something at a glance:
| You see | It means |
|---|---|
C-3f2a-1 … C-3f2a-800 | a founder's early work — low numbers under their shard |
C-9b7c-1 … | a contributor who joined later — a fresh sequence under a new shard |
| a gap in a sequence | a deleted feature; the number is retired, never handed out again |
legacy C-041 (flat) | a pre-sharding id — still valid; only new ids are sharded |
The ledgers auto-merge. yay init (and yay gate) writes a .gitattributes that marks the append-only records — lock.json, attest.json, rejections.json — as git merge=union, so git concatenates both sides' entries instead of raising a conflict. Trust is matched by content hash, not by line order, so a unioned ledger verifies exactly the same. (The roster is a chained governance log and is left to merge deliberately.)
After a merge or pull, run yay merge. It re-verifies and surfaces the only two things a merge can leave behind:
yay merge # re-verify + report collisions and Cells needing re-signing
- Id collisions — impossible for sharded ids; reported only if two legacy flat ids happened to clash. Fix: renumber one side to a fresh
C-<shard>-n(seeyay id), then re-sign it. - The same Cell edited on both branches — a genuine semantic conflict, like any merge. Git flags the source conflict; you reconcile the code + spec, the Cell drops to Unsigned (its old signature no longer covers the new bytes), and you
yay signthe reconciled version.
The key guarantee: a botched merge never goes green silently. Anything unresolved shows up in yay merge / yay verify and keeps the gate blocked. In the common case — two people adding different Cells — merges are seamless: no collisions, no renumbering, no re-signing.
Signing policy (required signers, non-delegable, more)
Optional and neutral by default. A policy adds constraints in .yaylayer/policy.json; it is owner-signed into the roster, so the AI can't quietly relax it. Rules match a Cell by path (glob), tag, and/or module. See Security for what each enforces.
yay policy --init # write a neutral, commented template
yay policy --set # owner-sign the current rules into effect
// .yaylayer/policy.json — "rules": [ … ]
{ "match": { "path": "**/auth/**" }, "signer": "Sara Olsen" } // require a person
{ "match": { "tag": "payments" }, "delegable": false } // never auto-approve
{ "match": { "tag": "sensitive" }, "inert": "block" } // dead code blocks the gate
{ "match": { "tag": "sensitive" }, "coverage": "full" } // every branch must be exercised
{ "match": { "tag": "sensitive" }, "predicate": "declared" } // every branch predicate must trace to in:
{ "match": { "path": "vendor/**" }, "ignore": "source" } // keep vendored code out of the gate
Foundation seal (protect your fixed core)
The foundation seal is an owner-signed baseline of the FIXED core (Constitution, CI workflow, .gitignore/.yaylayerignore, protocol files); any later change to it is revealed at yay verify. Offered at yay init/adopt; re-run yay protect to re-seal after a change you made on purpose (the dashboard's 🛡 Re-seal foundation button is the same, phone-signed). Ordinary code is never touched.
yay protect # create / re-seal (posture defaults to guarded)
yay protect --mode strict # drift BLOCKS the gate (vs guarded = warn)
yay protect --ignore "docs/**" # exclude an expected-churn path (the exclusion is itself signed)
yay protect --add <glob> # watch another path · --remove to stop · --off to disable
Request a change (from the dashboard) — and what a REQ is
The dashboard's ➕ Request a change button queues a plain-language request in .yaylayer/requests.json (local-only, git-ignored), numbered REQ-001, REQ-002, … Your AI drains the inbox with yay requests and turns each into a polished Brief + Cells to sign — you never write the Brief yourself. It's an asynchronous handoff: nothing is pushed to your phone until the AI has drafted something signable.
yay requests # the AI's inbox of queued requests
yay requests done <id> # clear one once it is signed / folded in
A REQ is not a governance artifact. It is not a Cell (C-…) or a Brief (A-…) — it carries no signature, no hash, no provenance. It is just a to-do note for your AI. The real, signed record is the Brief + Cells the AI drafts from it. REQs are ephemeral and local: they live only on the machine that made them, never commit, never merge, and are deleted when done — which is why they use plain sequential ids (REQ-001) rather than the collision-free shard scheme that Cells need. Think of it as the scratch note that starts a change; the Brief and signature are what last.
Preview (run scripts from the dashboard)
The dashboard's ▷ Preview lists your package.json scripts and runs any of them (dev server, build, lint) in the background, auto-detects the localhost URL, and shows an Open link + live output with a Stop button. It runs on the computer hosting the dashboard, so open it there.
Check an attestation (no install needed)
Anyone can confirm a verifier attestation is genuine at yaylayer.com/verify — entirely in the browser, nothing uploaded. Paste the contents of .yaylayer/attestations/<hash>.json; it checks the content hash, the verifier's signature, and that the declared capability matches the canonical registry. See Security.
Re-verify preserved history when the verifier improves
A better verifier shouldn't only help new code — it can re-judge everything you've ever trusted. In Durable mode, yay reverify --all reconstructs every preserved state, re-runs today's verifier over it, and diffs each Cell against its original verdict — an upgrade report ("42 old Green Cells would now be Yellow under capability 1.2.0"). It is read-only and keyless: the re-verdict is a deterministic recomputation, so anyone can look without any signing key.
yay reverify --all # the upgrade report (keyless); --since <date> · --eligible · -o file · --json
yay reverify --all --attest # mint SIGNED, append-only reverification records (needs the verifier key)
yay reverify posture strict # grandfathering: off (default) · guarded (warn) · strict (block)
--attest records each re-assessment as a new immutable event beside the old — the original approval stays historically valid; nothing is rewritten. The posture decides enforcement when the capability advances: off grandfathers all history (default), guarded makes yay verify warn, strict blocks the gate until preserved history is re-verified under the new capability. It gates on whether the signed record exists — never on holding a key — so the keyless report is always available. Scope it to crown-jewel Cells with an owner-signed policy rule { "match": { "tag": "sensitive" }, "reverify": "latest" }; unmatched Cells stay grandfathered. (Plain yay reverify, with no --all, re-attests just the current tree.)
Dashboard
yay dashboard --open # live map + phone relay; Briefs / Tags / Policy tabs
The Briefs tab is a history lens — open a Cell through a Brief to see it as that Brief signed it (As signed / Current / What changed / Timeline). yay map writes the same view as a static, read-only HTML file (no server, no live controls) — handy to commit or share.
08 — CLI reference
Commands
yay init [dir]- Guided setup. Flags:
--key,--relay/--lan,--name,--tags,--adopt,--constitution,--plan,--provider,--durable(Durable mode). yay sign [--cell IDs]- Approve the current specs.
--brief(required),--title,--tags,--name,--check,--phone/--local,--no-brief. The seal references the verifier attestation when one covers the tree. yay verify- Colour every Cell.
--strict(CI),--dir. Reports whether a signed attestation still covers the tree. yay attest [list|verify]- Mint / list / re-check the signed verifier attestation over the current verdict. Refuses a blocked gate (
--force) or a capability drift. yay grant- Signed capability envelope:
--for,--count,--cell,--allow,--deny,--allow-tag,--deny-tag,--max-risk,--child-grants,--no-guard(lift the default auth/payments/secrets/deploy/CI guard),list,revoke [id]. yay ratify [--sign]- List / human-sign delegated Cells — shows grant scope + boundary report, TOCTOU-safe.
--reject --reasonrecords a signed rejection. yay dashboard·yay map·yay gate·yay constitution·yay status- Live panel + relay · static map · CI gate (
--for github|azure|gitlab|bitbucket|gitea|gerrit) · write the Constitution · summary. yay ask "<question>"- Ask the configured (System-Plan) LLM about THIS repo + the manual — primed with the live project state (Cells, states, briefs, grants, rejections, gate) and the manual text. Also the Ask tab in the dashboard. Needs an LLM key in
.env. yay tags·yay policy·yay batch·yay adopt- Tag pool · signing policy (
--setowner-signs;inert/coverage/predicate/delegablerules) · batch barrier · adopt existing code. yay id·yay merge- This clone's Cell-id shard + next id (ids are
C-<shard>-n, per working copy, so they never collide on merge) · post-merge health check: re-verify and list any id collisions or Cells needing re-signing. See Teams: Cell numbering & merges. yay keygen·yay pair·yay enroll·yay invite·yay revoke·yay reroot- Create key · pair phone · enroll a signer · mint a join link · revoke a key (owner-signed; refuses to leave zero owners) · retire the trust root and establish a new one (last-resort recovery — see Recovery).
yay protect- Create / re-seal the owner-signed foundation seal over the fixed core files.
--mode guarded|strict,--add/--remove <glob>,--ignore <glob>(signed churn exclusion),--off. A broken seal warns inyay verify, the map, and the dashboard. yay reverify [--all] [--attest] [posture …]- Re-attest the current tree, or
--all: sweep preserved history through today's verifier → a keyless upgrade report (--since·--eligible·-o·--json);--attestmints signed, append-only reverification records;posture off|guarded|strictsets grandfathering enforcement. yay witness·yay metrics·yay capability·yay archive- Integrity witness · earned-autonomy metrics · derived capability + drift check · Durable encrypted archive (
installhook,--restore,--forget,--verify).
09 — Security model
How trust is protected
YayLayer's guarantees are real, but they are conditional. Without the setup below, YayLayer is advisory, not enforcing — it will help you work spec-first and show you colours, but it will not actually stop unsigned, mismatched, or tampered code (or a rogue reroot). Don't assume you're protected until all of these hold:
- The CI gate on a protected branch.
yay gateand GitHub branch protection: require thegatecheck, block force-pushes, and keep the trust root pinned. The local hook is only fast feedback — the CI gate is the real enforcement point. Without it, nothing truly stops bad code reachingmain. - Sign on your phone, not on disk. Phone signing keeps the key off the AI's machine, so a compromised machine or rogue agent cannot sign as you — and the phone re-hashes what it shows and refuses to sign a mismatch, so you can't be tricked into signing something other than what you reviewed (WYSIWYS). The Local keystore is convenient but the key lives on disk and signs on the same machine as the AI, so it can't offer that out-of-band guarantee — treat it accordingly.
- Git + a trusted, protected remote. The guarantees, the provenance, and recovery all assume git with a protected
mainon a host you control. Local-only means no enforcement and no clean copy to fall back to. - Protect your GitHub account (2FA). Branch protection and the pin live in your GitHub settings — a separate trust domain. If your account is compromised, so is the enforcement.
- Keep your 24 words offline. They are lossless recovery; without them, recovery means a disruptive reroot.
Miss these and YayLayer is still useful — but it's guidance, not a guarantee. You never have to guess which you're in: yay verify prints a live Protection: ENFORCING / ADVISORY ONLY readout with a per-item checklist (CI gate · signed root · phone signing · foundation seal).
Separation of duties
No actor grades its own work. The agent proposes, the verifier measures, the human authorizes. The agent is the untrusted producer — it may state what it made, never that it's correct or approved.
How deep verification goes
In a proven-tier language, Green is earned by execution, not inspection. Several checks stack:
- Behavioural prover — runs each pure Cell against its
ensureswith spec-derived inputs. - Mutation grading — mutates the code to see whether the spec's tests would catch the change; a spec too weak to catch its own mutants is capped at Yellow and names a surviving mutant.
- Spec-only adversary (on-demand — not a gate check) — an LLM that sees only the spec (never the code) proposes inputs to break it, run against the real code; a deterministic checker re-evaluates the
ensureson each, so a "break" is machine-confirmed (no LLM-judgement false positives). Eligible on pure,ensures-bearing Cells; needs an LLM key. Crucially it does not affect the gate: you run it (yay adversary, or the dashboard button), and a break tells you to strengthen the spec or fix the code — it never changes a Cell's colour or blocksmainon its own (that's what the deterministic checks above do). - Inertness check — deletes each branch and re-runs every spec-derived test; a branch nobody misses (dead weight or a dormant payload) is flagged, with policy able to escalate it to gate-blocking.
- Literal-seeding — harvests the constants a branch compares against, builds the exact input that fires a trigger, and turns a payload that contradicts the promise Red with a concrete reproduction (a Red-only lane — it convicts, never acquits).
- Branch-exercise honesty — counts which branches the spec-derived inputs actually executed and shows the boundary as a badge ("proven — 5/7 branches exercised"); never a downgrade by itself, but policy (
coverage: full) can demand 100% for crown-jewel scopes. - Undeclared-input predicate provenance — flags a branch that keys off a parameter the Cell's
in:never declares (if (mode === 'admin')whenin:lists onlyitems). This is the third prong of the pincer — inertness catches a branch that does nothing, literal-seeding catches a magic-constant gate, and this catches a branch that keys off an undeclared input (a hidden mode/flag), which the other two structurally miss. Deterministic, JS/TS-first, Yellow by default; the fix is to declare the input inin:(or athrows:/ensurescase) — the spec-strengthening loop — and policy (predicate: declared) can make it gate-blocking.
// C-071 in: items: {price,qty}[] ← `mode` is NOT declared
function invoiceTotal(items, mode) {
if (mode === 'internal') return 0; // ◈ Yellow — undeclared-input
return items.reduce((t, i) => t + i.price * i.qty, 0); // predicate: branch keys off `mode`
}
// fix: add `mode` to in: (so the human reviews & signs that it steers the code), or prune the branch
The honest boundary: a payload woven into live, exercised, promise-relevant logic is still theoretically constructible; these raise the bar a long way without claiming impossibility, and are strongest on JS/TS/JSX.
What the verifier supports today
The capability set is derived — a fingerprint is computed over exactly the list below, so an attestation can never claim more than the verifier actually does. See it live in the dashboard's Capabilities tab, run yay capability, or read the machine-readable registry at yaylayer.com/capabilities.json. The snapshot below is capability v1.2.0 (the tab/registry are always the source of truth).
| Behaviourally proven → machine Green | JS/TS functions · React components (JSX/TSX) · Python · Ruby · PHP — a pure function with a checkable ensures is run against it (Ruby/PHP: top-level/module functions today). |
| First-class effect nets | JavaScript/TS · Python · Ruby · PHP · Solidity · Rust · C# — a pure: yes Cell that actually does I/O is caught Red in each, in its own idioms. |
| Security & quality checks | ensures-prover · mutation-grading · inertness · literal-seeding · branch-coverage · jsx-render-prover · predicate-provenance |
| Policy rule kinds understood | required-signer · non-delegable · inert-level · coverage-full · predicate-declared · ignore-source · reverify-latest |
Anyone can confirm a specific attestation without installing anything at yaylayer.com/verify — it checks the content hash, the verifier's signature, and that the declared capability version matches this registry. Adding or removing anything above is a version-forcing change (a MINOR bump for a new check, MAJOR if the meaning of Green changes), so the fingerprint moves and yay attest refuses on drift — the verifier stays honest about its own reach.
Three keys, kept separate
Human phone key ("a person decided") · Verifier key ("the machine measured") · Artifact content hashes ("the exact thing"). A ratification references the verifier attestation hash but is signed by the phone key.
Never ship the verifier's signing key inside the npm package. A public key lets anyone forge a Green and makes the whole machine-attestation half worthless. The verifier key is project/CI-scoped: machine-held, gitignored, with the public key pinned in config. A signature carries trust only if its key is not universally available.
Phone signing
The human's private key is generated on the phone and never leaves it — secure hardware, biometric per signature. The AI's machine only sees the public key. A compromised dev machine or rogue agent still cannot sign as the human.
The phone doesn't just display a spec and trust the laptop — before you can Accept, it recomputes each Cell's sha256 on-device from the exact spec block it's showing and refuses to sign unless that hash equals what your signature would bind. The review you read is rendered from those verified bytes, not from anything the laptop could style independently. So a compromised laptop or hijacked AI cannot show you one spec and have you sign another — on any mismatch the phone hides the Accept button and warns you. It runs even over plain HTTP (a self-contained hash on the phone), so there is no silent gap, and it applies identically to LAN and relay signing. Every kind of phone signing is covered, not just Cell approvals: for governance actions — issuing a grant, sealing the foundation, enrolling/revoking a signer, re-rooting, setting policy — the phone renders the screen from the exact event it signs (heading, fields, and the raw signed bytes) and refuses if no event is present. What you see is always what you sign.
Why the phone, when the AI already shows the Brief and Cells in the harness? Because what the AI prints in the terminal is the untrusted producer describing its own work — the one thing that can't be the check on itself. A hijacked AI could display one thing in the harness and hand a different thing to be signed. So the check lives on a separate device: the phone re-derives and re-hashes what it will sign with a key the AI can't touch, so it can't be fooled by anything the harness shows. Trust in three layers, weakest to strongest: (1) the AI's harness preview — convenience, no cryptographic weight; (2) the local yay sign review — computed by the tool from the files on disk, independent of the AI's chat, but still same-machine; (3) the phone — fully out-of-band, the only one that holds even if the laptop and AI are entirely compromised.
The gate & Pink
Enforcement lives in CI (yay verify --strict), not a local hook the AI could disable. Any unit with no spec, or loose top-level code, shows Pink and blocks — you can't smuggle ungoverned code past the gate by simply not writing a spec.
Injection resistance
Injected un-specced code → Pink. Injected effect in a pure Cell → Red (line pinpointed). Behavior contradicting the spec → Red (counterexample). Tampered seal → Unsigned. Injected code can't reach Green without matching an approved spec or a fresh human signature.
Malformed markers fail safe
A broken spec marker can only make code look more suspicious, never less. An unterminated marker or a duplicate id is a reported problem that blocks the gate; the code it should have governed then falls to Pink because the coverage net enumerates every unit independently of the markers. A typo'd marker is simply unrecognised, so its code is un-specced → Pink. Editing a spec block (broken or not) changes its specHash, so the seal no longer matches → Unsigned. Marker games push code toward Pink / Unsigned / a problem — never toward a false Green.
Portable attestations & /verify
An attestation is self-verifying: its signature and hash cover its own bytes, so anyone can check one offline or at yaylayer.com/verify — the content hash, the verifier's signature against the pinned key, and the declared capability against the canonical registry. No server, account, or upload; trust in the signature is trust in that project/CI-scoped verifier of record.
Silent-drift & the integrity witness
A validly-signed Brief whose Cell has since drifted must not read green — the Briefs view shows covered-Cell health and a "what changed" diff. yay witness cross-checks the ledger against the tree — the attestation chain, the spec archive, and whether the latest attestation still covers the code — so git-history rewriting shows up as "the code git now claims existed here isn't what YayLayer verified."
The foundation seal — tamper & corruption evidence for the core
Ordinary code changes freely — that's dev work, governed by the normal spec → sign → verify gate. But a handful of files define how the AI and the rules operate and should stay fixed in ~99% of cases: the Constitution, the CI gate workflow, .gitignore / .yaylayerignore, the protocol files. The foundation seal is an owner-signed baseline of that fixed core, so any change to it is revealed at verify time. It is detection, not prevention — and it is cause-agnostic: a hijacked AI, disk/bit-rot corruption, a bad merge, or a fat-fingered edit all surface the same way, because the current bytes no longer match what the Owner last vouched for. The baseline is signed by the Owner's phone key, which never touches the AI's machine — so it's math a rogue agent can't forge, and you're never left blind to a change in the crown jewels.
It watches two kinds of drift:
- Content drift — the hash of each sealed core file. For the AI-rule files it hashes only the managed
∷ YAYLAYER:BEGIN…ENDblock, so your own guidance around it stays free. - Structural drift — the tracked file-set of watched zones (repo root top-level,
.github/,.yaylayer/): a new or removed file is revealed — a dropped payload, a rogue workflow, a file quietly untracked. This complements the Pink net: Pink catches un-specced code; the seal catches unexpected files of any type.
Why .gitignore is sealed: the structural watch keys off the tracked set, so .gitignore controls the blind spot — adding payload.js to it would make the file untracked and invisible. Sealing .gitignore means any change to what is hidden is itself revealed. (Untracking an already-sealed file is caught too — it vanishes from the zone set.)
Tracked files that legitimately churn inside a watched zone (a committed CHANGELOG, a version file) can be excluded — but the exclude list lives inside the owner-signed seal, so adding an exclusion is itself an owner-vouched act. A rogue AI cannot add ignore: my-payload.js without the phone key; each exclusion is a deliberate, signed blind spot. (Gitignored files like .env are already outside the tracked set — you never list those.)
Postures — chosen at yay init / yay adopt
| Posture | Seal | On drift | Gate |
|---|---|---|---|
| Off | none | — | unaffected |
| Guarded (default) | on | loud warning banner | still passes (informational) |
| Strict | on | warning | blocks until re-sealed |
The only difference between Guarded and Strict is whether foundation drift fails the gate or merely warns; everything else — the normal Red/Unsigned/Pink gate, signing, grants — is identical across postures. Default is Guarded: near-zero daily friction (it only speaks when the fixed core moves), maximum awareness. Secure by default, yours to tune.
Un-removable. The seal is an owner-signed, append-only event pinned via the trust root (the roster mechanism), so it can't be silently deleted or switched off — removing it breaks the pinned chain and is itself flagged. Only a human Owner can create it, re-seal it, or turn it off.
Re-sealing
When drift is revealed, you investigate with the diff the seal shows, then — if the change was legitimate (you edited the rules; you fixed a disk fault) — you re-seal: yay protect re-signs the current state on your phone. Only a human with the Owner key can clear the alarm; the AI can't (no key). Each seal is an append-only signed event, so you also get an audit trail of the foundation itself ("re-sealed after disk-corruption fix"). It's ratification, applied to the crown-jewel files.
Commands
yay protect (create / re-seal), --mode guarded|strict, --add / --remove <glob> (tune the sealed set), --ignore <glob> (a signed churn exclusion), --off (owner-signed disable). Re-seal from the dashboard with the Re-seal foundation button. A broken seal shows a warning in yay verify, the map, and the dashboard.
Incident recovery — a compromised machine or remote
Because YayLayer is built on git, every trusted clone is a full, signed copy — so no single location is a single point of failure for the content. Tampering with either side is detectable and recoverable from the other.
- Compromised local machine → restore from GitHub. A rogue reroot or edit is only local until pushed; on push the CI gate blocks it (unsigned / mismatch / TRUST-ROOT MISMATCH), so it never reaches a protected
main. Recover by discarding the local changes or re-cloning onto a trusted machine. The blast radius of a compromised machine is only the unmerged local state you throw away. (In phone-signing mode the key was never on the machine, so it can't sign at all; in local-keystore mode, also rotate the key.) - Compromised remote → restore GitHub from a trusted local. A poisoned remote isn't silent: your local clone diverges on
git fetch(rewritten history = different SHAs), andyay verify/yay witness+ the signatures flag any content that no longer matches the signed ledger. Force-push the true history from a clean clone (or recreate the repo and push). The restored copy is self-verifying — the roster, signatures, and pinned root all travel inside the repo, so you don't have to trust the copy, you verify it against the trust-root fingerprint you hold out-of-band (your records / your eyes — the bedrock anchor).
The CI pin, branch protection, and collaborator list live in GitHub account/org settings, not in the repo. Restoring content fixes the code and roster; if your account was compromised you must regain it separately — 2FA recovery, revoke the attacker's access, reset branch protection. YayLayer's cryptography secures the content; it cannot secure your GitHub login. So: content is mutually recoverable (local ↔ remote); the account is a security domain you protect on your own.
10 — Teams, roles & recovery
Who may sign, and how to recover
The foundational trust layer the rest stands on — who may sign, how they join, and how to recover when keys change.
Two roles: signer and owner
| Role | Sign Briefs/specs | Enrol / revoke | Issue Autopilot grants | Sign policy | Re-root |
|---|---|---|---|---|---|
| Signer | ✅ | — | — | — | — |
| Owner | ✅ | ✅ | ✅ | ✅ | ✅ |
A signer approves intent (specs + Briefs, and ratifies delegated work). An owner can do that plus govern the project — add/remove signers, issue grants, sign policy into effect, recover the root. The first identity is the genesis owner; grant owner only to people who should govern.
The roster
Who-may-sign is an append-only, owner-signed event log (genesis → enrol → revoke …, prev-chained ed25519), with the trust root pinned in CI. yay verify derives the current identities → keys → roles from it and checks every seal against it. Enrolment can't be forged (needs an owner signature; the root is pinned outside the AI's reach), and revocation stops future signing without rewriting past attributions.
Adding a teammate
yay invite "Bob" — a 30-minute one-time link; Bob's device makes his key, you approve on your phone by matching a 6-digit code. Or yay enroll --name Bob --pubkey <b64> when you already have the key. Both write an owner-signed roster event; --role owner|signer (default signer).
Removing a key · routing · methods
- Revoke:
yay revoke --name X(owner-signed;--pubkeyfor one key) — refuses if it would leave no owner. - Signer routing:
yay sign --name "<teammate>"routes the request to their inbox over the relay;yay sign --check [id]collects the returned signature — so "Lisa must sign the security Cells" works even when Lisa is elsewhere. - Signing methods (per project, at
yay init): Local (encrypted keystore on this machine), Mobile · LAN (phone over Wi-Fi), Mobile · Relay (phone via end-to-end-encryptedrelay.yaylayer.com, off-LAN). The AI's machine only ever holds public keys.
Recovery — restore vs revoke vs reroot
Which path you take depends on why you lost access and whether you're solo or on a team. yay reroot is the last resort — a deliberate trust discontinuity, not the everyday answer.
| Situation | What you do | Reroot? |
|---|---|---|
| Lost phone, still have 24 words | Restore — the mnemonic re-derives the same key. No discontinuity, nothing to re-sign. | No |
| Lost phone, no words, solo | yay reroot — establish a brand-new trust root. | Yes |
| Lost phone, no words, team | Another owner revokes the old key + enrols a new one (root untouched). | No |
| Key compromised, solo | yay reroot — restore is useless here (see note). | Yes |
| Key compromised, team | Another owner revokes the leaked key + enrols a fresh one. | No |
Restoring from your 24 words re-installs the same key — which the attacker also holds. For compromise the goal is to make the leaked key worthless, so you must retire it (reroot) or revoke it (team), not restore it. Restore is only for a lost key you alone can bring back.
So the rule: yay reroot = "I need a brand-new trust root because the current one is unrecoverable or compromised, and there's no other owner to revoke it for me." On a team, revoke/enrol almost always replaces it; solo, reroot is the fallback.
By design reroot cannot require the old key (the whole point is that it's gone). But that does not make it a takeover path, because a reroot only proposes a new root — the CI root-pin blesses it. An unauthorized reroot changes the root fingerprint, so yay verify reports "TRUST-ROOT MISMATCH" and the gate blocks — a loud alarm and hard stop, not a silent swap. To make a new root real, a human must repoint the pin via yay gate in GitHub's branch-protected settings, which live outside the repo and the AI's reach. So an attacker with repo-write can trip the alarm but can't take over without also compromising your GitHub settings — two separate trust domains — and reroot can't forge or rewrite past signatures (old approvals stay attributed to the archived root). The trust ultimately rests on the CI pin only you control; reroot is subordinate to it. The one real gap: with no CI gate/pin (solo, local-only), a reroot has no backstop — but then nothing is truly enforced anyway. After a legitimate reroot: re-sign specs, repoint the CI pin, re-enrol the team, and re-establish the foundation seal under the new root.
A reroot is never silent. It takes a stated reason (yay reroot --reason "keys lost" / "compromised in the 2027-06-24 breach", prompted if omitted) and stamps the rotation into the new, self-signed genesis — supersedes (the old root fingerprint), rerootedAt, and rerootReason — so it's tamper-evident and shows up in the audit. yay verify prints it ("trust root … · rerooted from … on … — <reason>"). Like every YayLayer event, it's ISO date-stamped. And provenance is preserved, not erased: the old roster is archived, so past signatures stay valid against it and the record reads as a labeled seam — "root rotated on <date> because <reason>" — with both eras verifiable.
11 — Limits & non-goals
What YayLayer does not (yet) do
Being honest about the edges is part of the design — YayLayer never dresses up unverified code as proven. These are the current boundaries.
Proof ceilings by language
Behavioural proof (execution that earns Green) covers JS/TS/JSX/TSX and pure top-level functions in Python, Ruby and PHP. Solidity, Rust and C# are static-only (effect net) — behavioural proof needs a language harness (EVM / cargo / dotnet) that does not exist yet, so a good Cell caps at Yellow. In Ruby/PHP the provers resolve top-level/module functions only, so class/instance methods and framework-coupled code (Rails) cap at Yellow too — Rails can adopt now for governance, the gate and provenance, with proof on its pure slice.
Grant constraints are enforced by detection
A grant constraint is meaningful only if the verifier can detect its violation in the output. Scope constraints (allowed/denied paths, cells, count, expiry, max-risk) are enforced today. Content constraints (dependency additions, network/effect limits) are recorded in the envelope but only become enforceable as the verifier gains the matching detectors — never promise a boundary you can't verify.
Single required-signer, not multi-sig
Signing policy can require a specific person per path/tag/module. M-of-N multi-signature for crown-jewel Cells is not yet available.
Verification, not sandboxing
YayLayer measures the output of the agent; it does not sandbox the agent's execution. The authorization boundary lives outside the agent's reach (owner-signed policy, CI-pinned trust root), and the verifier is the backstop that catches an agent overstepping — but the model is detection at verify/ratify time, not runtime confinement.
Non-goals (by design)
- Not a code-approval system — a refactor that preserves the spec needs no new signature.
- Not a blockchain — just an append-only hash chain; no tokens, no consensus.
- Not an AI grading itself — the verifier is deliberately non-AI: bounded, reproducible, versioned.
- Not a hosted service — verification and provenance are local + CI; the keyless attestation checker + capability registry at
yaylayer.com/verifyare the only hosted pieces (an optional public-record notary is not part of the core).
12 — Design rationale
Why it's built this way
The reasoning behind the choices an implementer will feel most.
- Separation of duties
- No actor grades its own work: the human authorizes intent, the machine measures conformance, the agent only produces. Collapsing any two of these is the failure mode YayLayer exists to prevent.
- Two chains — sign intent, not bytes
- The human signs the spec + Brief, never the code bytes, so a spec-preserving refactor needs no re-approval. Authority (human) and verification (machine) are kept orthogonal, never merged into one traffic light.
- Green is an event, not a property
- Code is Green relative to a spec + verifier + semantics + time. A stronger verifier later can append a new assessment without making an old Green fraudulent — which is why history is never rewritten.
- Append-only, content-addressed
- Every material object is stored by the hash of its content, deduped, and never mutated in place. Integrity is free — the stored body must hash to the value the seal signed.
- Preserve every spec, always
- Specs are tiny and irreplaceable, so every signed spec is archived in both Standard and Durable modes — the "as signed" view never depends on git being intact.
- The verifier key is never shipped
- A verifier signature is worth something only if its key isn't universally available; a project/CI-scoped, gitignored key is the minimum honest form.
- Governance is first-class
- The real cost of "keep code forever" is governance, not disk — so Durable mode ships with encryption under a project-held key, owner-signed retention, a pre-archive secret scan, and honest deletion tombstones.
- TOCTOU-safe ratification
- Ratification vouches for the exact reviewed bundle: the hash is recomputed at sign and refused on any drift, so a human can't review one thing and unknowingly sign another.
- Constraints gated by detectors
- A grant only carries constraints the running verifier can actually detect; an unenforceable boundary would be theatre.
- Deterministic proving
- The prover has no RNG (enumerated inputs, fixed mutants), so the same environment yields an identical verdict; attestations record the environment and claim "Green under this verifier + environment at this time."
- Naming reflects control
- "Autopilot" / "Delegated execution", never "Freedom mode"; "Delegated · Awaiting ratification", never "auto-approved." A control product shouldn't borrow the vocabulary of removing control.
13 — Glossary
Terms
- Agent
- The AI producing Briefs, specs, and code — the untrusted producer.
- Attestation
- An immutable, content-addressed record of a claim; verifier attestations are also signed.
- Autopilot / Delegated execution
- Operating under a grant — the AI produces within a bounded, human-signed envelope, to be ratified later.
- Brief
- The human-readable headline over a change-set; signed with the specs; never earns Green.
- Capability envelope
- The signed set of constraints in a grant (paths, deps, max-cells, risk…).
- Cell
- A unit of code plus its inline spec block — the atomic thing YayLayer governs.
- Content-addressed
- Stored/looked-up by the hash of the content; identical content stored once.
- Durable / Standard
- Whether source is archived (encrypted) in the provenance store, or referenced from git.
- Forward signing
- The default — signing the spec before the code exists.
- Gate
- The CI check that blocks Red / Unsigned / Pink from
main. - Grant
- An owner-signed authorization for the agent to act autonomously within an envelope.
- Green / Yellow / Red / Unsigned / Pink
- The verification states (see Concepts).
- Integrity witness
- Using the ledger to detect git-history rewriting.
- Non-delegable
- Categories requiring human approval before execution, not execute-then-ratify.
- Provenance
- The full chain: who had authority, its source and limits, what the agent did, what the verifier established (and under which semantics), when a human ratified, and what changed after.
- Ratification
- Retrospective human authorization for grant-produced work, with code + evidence present.
- Roster
- The append-only, owner-signed log of trusted signers.
- specHash
sha256(normalized spec block)— what the seal signs and the archive keys on.- Tombstone
- A record that a stored object was deliberately removed, preserving the chain while honoring deletion.
- Triad
- Human / Agent / Machine verifier.
- Verifier
- The deterministic, non-AI system that evaluates code against specs; versioned and cryptographically identifiable.