YayLayer — a protocol for provable, signed AI code

Own the code your AI writes. Your AI can write a thousand lines while you blink. YayLayer makes every change spec-first and human-signed — so you can prove, in plain language, exactly what you approved. Think of it as a building permit for AI-written code: approved blueprints first, an inspector who checks the build matches the blueprints, and you can’t ship until it passes.

The core idea: sign the promise, not every line

Every unit of code — a Cell — carries a mirrored spec in its comments. You sign the spec’s hash on your phone, so the AI can refactor freely and only a changed promise needs re-approval. A checker proves that the code matches the spec and paints your whole project by state.

How it works: spec → sign → build → verify → gate

  1. Spec first. The AI writes a Cell’s spec block before any implementation. No code it writes is trusted until the spec is signed.
  2. Present a Brief. It drafts a Brief — what you asked for — and presents the change-set with the colour it expects each Cell to earn.
  3. You sign on your phone. It runs yay sign; the request appears on your phone. You Accept & sign, or Send back with a note for the AI to revise. The private key never leaves the device.
  4. Verify. The moment your signature returns, the AI implements the code and runs yay verify — static checks, a behavioural prover, and mutation grading.
  5. The gate. CI runs yay verify --strict. Anything Red, Unsigned, or Pink fails the check — it can’t reach main.

The AI can’t cheat — and can’t be told to

Authoring is split from approval by cryptography, not policy. A valid seal is mathematical proof that a human, holding a specific key, approved those exact bytes — not “the AI says it’s fine.”

Protected by math: a signature you can’t fake, move, or delete

A YayLayer seal isn’t a signed: true flag anyone can flip. It’s an ed25519 signature — a number only your private key can produce, over the exact bytes of the spec and the Brief.

Think of it as a tamper-evident seal plus a customs checkpoint: nothing enters your trusted codebase without a human signing the manifest. As a side-effect it resists prompt injection — the AI can be told to write anything, but it can’t get that code signed, and un-approved code shows up Pink or Unsigned and is stopped at the gate. It is not an antivirus and won’t stop a change you deliberately approve — the human stays the boundary.

Language support (honestly tiered)

Recovery — without a backdoor

Teams

Each person holds their own key; a signed, owner-controlled roster maps keys to names, so every seal attributes to a named human. Adding a signer is a privileged, owner-signed action — the AI can never enroll a key. Private keys never travel between people; you only exchange public keys. Revoking is the mirror, and it refuses to leave the project with zero owners.

Install & signing modes

Requirements: Node 18+ and git. Install once with npm install -g yay-layer, then yay init. Choose how you sign:

Then instruct your AI once with yay constitution --for <harness> (Claude, Cursor, Copilot, Windsurf, Cline, Gemini, AGENTS.md, or a generic copy) and just ask it to build. Common commands: yay init, yay pair, yay sign --brief, yay verify, yay dashboard, yay adversary, yay gate, yay map, yay adopt, yay enroll, yay revoke, yay reroot.

How it compares

YayLayer is a layer, not a rival. Tests check examples; types constrain shapes; design-by-contract is heavy and rarely adopted; code review is unaided reading with no proof; Copilot/Cursor rules steer generation; git tracks changes. YayLayer adds a human-readable contract, enforces no extra behaviour, requires a signed human review, and gates the result — proving the code matches the spec and attributing approval per unit. It complements GitHub Spec Kit too: drive authoring with Spec Kit, prove and sign the result with YayLayer.

When it’s a good fit — and when it’s overkill

Good fit: shipping AI-written code you can’t fully review line by line; trust and accountability matter (clients, teams, or sensitive code like auth, money, access control); you want a plain-language, signed record of exactly what you approved; you’re vibe-coding but want guardrails against scope creep. Probably overkill: a throwaway script or prototype; solo hobby code you already read every line of; you’re barely using AI to generate code.

Frequently asked questions

What is a Brief?

A Brief is the plain-language record of what you ordered — a short title (a headline, like a commit subject) over one or two sentences the AI drafts in its own words, plus the list of Cells it covers. It's what you sign: your signature is over the specs and the Brief together, so the Brief becomes an attributed, tamper-evident record of the intent behind each change — your project history in your own words, not just diffs. The AI drafts it; at signing you Accept & sign, or Send back with a note. Every Brief is tagged from your project vocabulary, kept to one concern, and browsable as a list, by tag, or a Cloud view (a card per tag) — in the dashboard or yay briefs. Small changes can batch into one Brief (default barrier 5); a big or sensitive change always gets its own.

How do I install and set up a project?

You need Node 18+ and git. Then: npm install -g yay-layer, git init, yay init. At yay init you pick how you sign (Local, Mobile-LAN, or Mobile-relay), then write the rules into your AI with yay constitution --for <your-tool> and just ask it to build.

How do I set up the CI gate that protects main?

Run yay gate. It writes a GitHub Actions workflow (.github/workflows/yaylayer.yml) that runs yay verify --strict on every PR and pins your trust root, then prints the one-time steps to make GitHub enforce it: (1) push the workflow to main first and let it run once — the check only becomes selectable after its first run; (2) GitHub → your repo → Settings → Rules → Rulesets → New branch ruleset, Enforcement Active, target the default branch (main); (3) tick Require a pull request before merging, Require status checks to pass → select gate, and Block force pushes → Create. Now nothing merges to main until the gate is green — no Red, Unsigned, or Pink (Yellow is allowed). Branch protection lives in GitHub settings only you control, so a leaked token or compromised machine can't switch it off. For solo/offline fast feedback, yay gate --hook also installs a local pre-push check.

Can I run my dev server or build from the dashboard?

Yes — the ▷ Preview button lists your package.json scripts and runs any of them (dev server, build, lint) from the dashboard. It spawns in the background, auto-detects the localhost URL and shows an Open link plus live output, with a Stop button; everything is killed when you stop the dashboard. It runs on and links to the computer running the dashboard, so open it there — not from a phone.

Which signing mode should I choose — Local, LAN, or Relay?

Local: key on this machine, passphrase-encrypted — simplest and offline but least private. Mobile LAN: key on your phone, phone and laptop talk over your Wi-Fi — most private, no third party, same network required. Mobile relay: key on your phone, requests travel through relay.yaylayer.com end-to-end encrypted — works from any network; the relay only shuttles ciphertext.

How do I add a teammate?

Run yay invite "Their Name". They open the link, set their name (your text pre-fills it, editable), create their key on their phone, and you approve on your phone after verifying a matching 6-digit code. Then commit and push .yaylayer/roster.json so everyone and CI trust them.

Do I have to agree their exact name beforehand?

No. The name is just a display label for their signatures — no identity check. What you type in yay invite only pre-fills their name field, which they can edit; you see and confirm the final name on your approval card.

What is the difference between a signer and an owner?

A signer can approve and sign Cells but cannot manage the roster. An owner can also invite and revoke others. You choose per invite. There is no special founder; you are just the first owner, and governance never lets the roster drop to zero owners.

Can I send an approval to a specific teammate to sign?

Yes — the signer router. Run yay sign --name "Sara" and the request is sealed to Sara's inbox only (over the relay); it appears solely on her on-duty phone (she opens it with yay inbox), never anyone else's. It is fire-and-return: you get a request id immediately and keep working — the covered Cells stay Unsigned and gate-blocked until she approves, then yay sign --check collects her signature back to you. Combine it with a signing policy to require a specific person for certain Cells (e.g. everything matching **/auth/** must be signed by your security lead).

When I am working, does my teammate get my approval requests?

No. A yay sign request goes to the phone paired on your machine and must match your key. Being in the roster means a teammate’s own signatures are trusted — they sign their own work from their own machine, attributed to them. Signing is per-person, per-machine.

How does a new teammate start working after I add them?

They install yay, clone the repo at the commit where they are enrolled (push the roster first), then run yay pair choosing their existing key (the one they made when joining, or restored from their 24 words), not a new one. Their laptop sees the key is already in the roster; then yay sign routes to their phone.

Do I have to commit and push after an approval?

For it to reach teammates and CI, yes: commit the .yaylayer/ change (the seal in lock.json, or roster.json for an enrollment) together with the code it covers, then push. Locally nothing breaks before you commit — yay verify reads your working tree. It fails safe: push code without its seal and the gate marks it Unsigned and blocks it.

I lost my phone but still have my 24 words — what now?

On a new phone, choose Restore from recovery phrase. The 24 words re-derive the exact same key, so every past signature still verifies and nothing needs re-signing. Set a new PIN and you are back.

I lost the phone and the 24 words — am I locked out?

The key is gone — there is no backdoor, by design. But you are not locked out of the project. Solo: run yay reroot to establish a new trust root, re-sign the current specs, and repoint the CI pin. On a team: another owner revokes your lost key and enrolls a new one. Governance never lets the roster reach zero owners.

Could someone just delete a signature and paste their own?

You can delete it, but you cannot produce one that verifies. A seal is an ed25519 signature over the exact spec hash plus Brief; only your private key makes a valid one. A different signature fails against your public key, so the Cell goes Unsigned and the gate blocks it. Who-may-sign is an append-only, owner-signed roster pinned in CI.

What exactly does my signature attest to — the spec, or the code?

The spec plus Brief only, never the implementation. The signed bytes are the spec-block hashes plus the Brief; no code hash is included. yay verify continuously re-derives everything from your real files and proves the code matches the spec. Edit the spec or Brief and it goes Unsigned; let the code drift from the spec and it goes Red.

Why is signing on the phone safer than on my computer?

Because of where the key lives. In local mode the key sits on the machine the AI runs on, so a compromised machine could sign anything. On a phone (or via the end-to-end encrypted relay) the key is on a device the AI never touches — even a fully compromised dev machine cannot produce your seal.

Does it protect against prompt injection?

Partly, as a useful side-effect. A prompt injection can make the AI write anything, but it cannot get that code signed — there is no key on its machine, and un-approved code shows up Pink or Unsigned and is stopped at the gate. Think tamper-evident seal plus customs checkpoint, not antivirus: it will not scan for malware or stop a change you deliberately approve.

How do I bring an existing codebase under YayLayer (adopt)?

yay adopt scaffolds a draft spec over every top-level function it finds (marked DERIVED — unconfirmed). You and your AI then prune each draft from descriptive to prescriptive — the spec becomes what the code should do, and code that overreaches goes Red for the AI to refactor down. Before the adoption is signed, two enforced rules kick in: (1) the tag plan must be finished first — a real vocabulary with at least 5 unique tags and no leftover Custom 1–4 placeholders; the AI picks each Brief’s tags from your plan, so yay sign refuses until the plan is real. (2) The adoption signs as a series of per-concern Briefs, not one bundle — “Adopt: authentication” tagged Security, “Adopt: checkout” tagged Business Logic — so day one of your ledger already reads as a map of the system by concern.

If an AI injects code that wasn’t there before, will YayLayer catch it?

In the common cases, yes — injected code lands in a gate-blocking state, and the AI has no key to turn any of them Green. Three ways it surfaces: (1) a new function or unit with no spec → Pink — verify enumerates every named unit, anything untracked is Pink, and Pink blocks the gate like Red (this is where an exfiltration payload usually lives — a new helper or a top-level call); (2) behaviour that needs a new or changed promise → Unsigned, and the AI cannot sign it back; (3) lines added to an existing signed function whose behaviour now contradicts its spec → Red — an undeclared side effect (network, filesystem, localStorage) in a pure Cell, or an output that breaks the ensures; verify re-derives code-versus-spec on every run, and the mutation grader and spec-only adversary hunt for behaviour the ensures doesn’t pin down. (4) a dormant payload → a two-check pincer — trigger-gated so it never fires under test. Inertness deletes each branch and re-runs every spec-derived test; a branch nobody misses is flagged Yellow (dead weight or a sleeper), route “prune it, spec it, or declare it” (throws:/perf:); policy escalates it to gate-blocking for sensitive scopes ({ "match": { "tag": "sensitive" }, "inert": "block" } — owner-signed) or relaxes it to a note. Literal-seeding goes further: it harvests the magic constant the branch compares against, builds the exact input that fires the trigger, and if the payload contradicts the promise → Red, unconditionally, with a concrete reproduction (code-derived inputs are a Red-only lane — they convict, never acquit; a Green only ever comes from spec-derived inputs). To survive both, a payload must be trigger-hidden-from-any-literal, not-inert, and contract-consistent. The honest boundary: a payload woven into live, exercised, promise-relevant logic is still theoretically constructible — these layers raise the cost, they don’t reach impossibility. Strongest on JS/TS/JSX. The signal is the state (Pink/Unsigned/Red/Yellow), not a line-level diff — there is no code snapshot in the seal, by design.

Do I need a .yaylayerignore, like a .gitignore?

In most projects, no — you never create one. The scanner only looks at code files (by extension), so images, docs, JSON, lockfiles and configs are ignored automatically; and it auto-skips node_modules, .git, dist, build, coverage, docs, and any dot-directory (.next, .venv). So a normal project has nothing left to ignore. You'd only add one to exclude source code you consciously keep out of the gate — vendored third-party code, generated files, or a legacy folder you haven't adopted yet. And that's a signed decision: a .yaylayerignore line hiding a source file turns the gate Pink (blocked) on its own; to keep it out you add an owner-signed policy rule { "match": { "path": "vendor/**" }, "ignore": "source" }. Hiding source from the gate must be a conscious, human-signed act — never a casual line the AI can slip in. It is never created automatically.

What languages does it support?

Machine-proven Green: JavaScript, TypeScript, JSX, TSX (functions and React components), plus Python, Ruby and PHP — pure top-level/module functions with an ensures, run in a subprocess (their classes and effects stay signed-only). First-class effect nets (a pure: yes Cell that does I/O is caught Red): JavaScript/TS, Python, Ruby, PHP, Solidity, Rust, C#. Signed-only (capped at Yellow): Go, Java, C/C++, Kotlin, Swift, Scala, Dart, Elixir, and the rest. Anything else whose comments use // or # works with its file extension enabled.

What does signed-only (Yellow) mean?

The trust layer fully works — the code is spec-mirrored, signed, attributed, mapped, and gated — but YayLayer has not machine-verified that the code matches the spec for that language yet. So the strongest colour it can reach is Yellow (signed, not machine-proven), never a false Green.

What happens to old Green when the verifier improves?

“Green is an event,” not a permanent property — it is never rewritten. When the verifier improves (a new prover lands, or a policy capability is added), yay reverify --all replays your whole preserved history through today's verifier and diffs each Cell against its original verdict — a keyless upgrade report (e.g. “42 old Green Cells would now be Yellow under the current capability”). It's read-only: the re-verdict is a deterministic recomputation, so anyone can look without a signing key. yay reverify --all --attest then records each re-assessment as a new signed, append-only event beside the old one — the original approval stays historically valid; nothing is overwritten. And it never fails your build by surprise: a grandfathering postureyay reverify posture off (default), guarded (warn), or strict (block until re-verified), optionally scoped to crown-jewel Cells — decides what a capability bump does at the gate. So a better verifier makes your history a compounding asset instead of quietly editing the past.

Do I have to pull out my phone for every tiny change?

No — that would be approval fatigue, the thing YayLayer works hardest to avoid. First, you sign intent, not edits: a refactor or tweak that keeps the same spec needs no new signature. Second, batch mode (on by default): when you ask for something small, the AI writes its spec + code so you can try it immediately and accumulates it into a pending batch, grouped per concern. When a batch reaches the barrier (default 5, set with yay batch 8 or in the dashboard) — or you're about to commit/push — it shows the batch with a proposed title and asks: close & sign, add one more, or keep going? So a run of small changes becomes one signature at a boundary you choose. A big change gets its own Brief right away; anything sensitive or behaviour-changing always gets its own Brief, never batched. You control the cadence: raise or lower the barrier, or switch batching off entirely for a Brief-per-change — from the CLI (yay batch 8, yay batch off) or the dashboard's Briefs tab. It's a project setting (in config.json), so it's remembered and shared with your team; turning it off changes only Brief granularity, never the signing or gate protections.

Can I let the AI sign on its own for a while? (Freedom mode)

Yes — issue a scoped, phone-signed grant: yay grant --for 2h --count 20. You approve the grant once on your phone; after that the AI auto-approves in-scope changes with no phone contact, until the time limit or the count runs out — whichever comes first (defaults 2h / 20). Scope it to named Cells with --cell C-012,C-013, or leave it open for all non-sensitive Cells. Commit .yaylayer/grants.json; the grant key stays in gitignored keys/.

Isn't unattended signing dangerous? What are the guardrails?

Three: (1) anything sensitive or code-pinned (auth, money, access control) always needs a real signature — a grant can never cover it. (2) Every auto-approval is stamped AUTO / not human-reviewed and queued; you reconcile them later with yay ratify (--sign to sign them for real). (3) Only a human can issue a grant — the AI can never grant itself freedom — and you stop it any time with yay grant revoke (list active ones with yay grant list).

What are Brief tags — do I have to use them?

At yay init you pick a tag vocabulary — or skip it. If you pick one, every Brief is tagged with 1–3 tags from that pool (the AI chooses them from the request's content), so you can later sort everything you've built by concern and by time. Browse your Briefs newest-first or grouped by tag — click a tag in the Dashboard Briefs tab to filter, use its Group by tag toggle, or in the terminal run yay briefs (--by-tag, --tag Security). There's also a Tags tab that groups every Brief by tag. The AI also keeps things tidy — if a request mixes concerns (say GUI and Security), it suggests splitting it into two focused Briefs. And if you disagree with the AI's tags, you can switch them (within the pool) on the phone when signing — that blocks Accept and sends the Brief back for the AI to re-issue with your tags.

What tag sets are built in — and can I make my own?

Six pre-built sets, each a curated vocabulary: Technical / code type (HTML, CSS, JavaScript, Frontend, Backend, API, Database, Auth, Testing…), Responsibility (UI, State, Routing, Business Logic, Data Access, Security, Performance…), Component kind (Component, Hook, Service, Controller, Model, Repository, Worker…), Layer (Presentation, Application, Domain, Service, Integration, Infrastructure…), App area (Header, Navigation, Dashboard, Form, Modal, Settings, Auth, Admin…), and Product system (Experience, Content, Domain, Identity, Data, Communication, Observability…). Or choose Custom — it seeds blank placeholders (Custom 1–4) you relabel. Either way you can switch sets, add, remove, rename and describe tags with yay tags or the Dashboard Tags tab; yay tags sets lists them in full.

Can I change a tag after Briefs already use it?

You can always add, describe, or relabel an unused tag. But renaming a tag that's already in a signed Brief is blocked — the tag is inside the signature, so past Briefs keep that exact label forever; renaming the pool would split your history into two names for one concept. Removing a used tag is allowed as deprecation: past Briefs keep it and still show under a Retired section, it just stops being offered for new Briefs. So relabel the custom placeholders before you use them.

What's the signing policy — and how is it different from tags?

Tags organize; policy enforces. A signing policy is optional and neutral by default. Add a rule and it requires a specific person to sign Cells matched by path glob, spec tag, or module — the CI gate then blocks any matching Cell that person hasn't signed. Rules are owner-signed into the roster, so they're tamper-evident and can't be quietly weakened. Edit and apply them in the Dashboard Policy tab (Apply owner-signs on your phone), or with yay policy. Policy also carries the inert-code strictness (the built-in security feature): inert code — a branch removable with every spec-derived test still passing — caps a Cell at Yellow by default; a rule escalates it to gate-blocking for sensitive scopes ({ "match": { "tag": "sensitive" }, "inert": "block" }) or relaxes it to a note for adopted legacy areas. The Policy tab shows ready-made templates for this, off by default.

Can the signer edit the Brief on the phone before signing?

No, and that's deliberate. On the phone you have two moves: Accept & sign, or Send back with an optional note. The Brief drives its Cells, so changing the wording can warrant changing the specs — and the phone can't touch specs. So a real change goes back to the AI: you send it back with a note (e.g. “rate-limit signup too, keep the rest” or “wrong approach, redo”), and the AI reconciles the whole change-set — Brief and Cells together — then re-presents it for a fresh signature. Refining the Brief's wording happens in the loop with the AI before you sign, never as a lone edit that could drift from what the Cells do.

What happens when I send a change-set back?

The Cells stay Unsigned (the gate keeps blocking them) and your note lands in the AI's tool. The AI decides scope from what you wrote: a small correction → it tweaks the affected specs + Brief and re-presents; something fundamentally wrong → it rebuilds the change-set from scratch as a new request. If you send back with no note, the AI won't guess — it tells you it came back empty and asks how to proceed.

Is YayLayer open source? Can I contribute?

Yes — YayLayer is MIT-licensed and built in the open on GitHub. It's early, so it's a good time to help shape the protocol. Contribution runs through GitHub Issues and Pull Requests; the protocol's source of truth is CONSTITUTION.md and standard/STANDARD.md. High-leverage areas: language adapters (so today's signed-only languages — Python, C#, Rust, Go, Solidity and more — can also earn machine-proven Green), teams & policy (M-of-N multi-sig, safe merge re-proving, a richer policy engine), real-world use (try it on a project and report rough edges — field feedback shapes the roadmap most), and docs & examples. Changes stay spec-first — YayLayer is built with YayLayer, so yay verify stays green — and non-trivial work starts with an issue to discuss.

Source on GitHub. MIT-licensed and built in the open — contributions welcome. A protocol for provable, signed AI code.