Repository navigation
Proposal: rename to access-layer-specs, the Access Layer specs home #29
Description
This picks up from #27, where Oskar proposed evolving zkspecs into the home for Access Layer specs. Here is the concrete version of that idea.
What we're asking for
- Buy-in on scope. One canonical repo for Access Layer specs, covering how people and their agents read, write, prove, delegate and exit, not just privacy.
- A repo owner. Someone accountable for process, lifecycle, and cross-domain consistency.
- Reviewers. One or two named people who review incoming specs.
Summary
Access Layer work produces specs and spec-like documents across project repos, HackMDs, papers, and research notes. There is no canonical place, so builders have to guess what the official behavior is, what is stable enough to build against, and where a new spec should start.
The repo has already moved into the ethereum org. We propose renaming it to access-layer-specs and making it that home. The name follows the org's convention (consensus-specs, execution-specs, cryptography-specs).
The repo defines rules, interfaces, profiles, lifecycle status, and where each spec originally comes from. It does not list tools or implementations, existing tool directories like ethereum/builder-resources cover that, this repo only defines the rules those tools implement. It does not compete with existing standards processes like EIPs/ERCs, W3C specs such as Verifiable Credentials, or the Tor protocol specs. It links to them, depends on them, or defines Access Layer profiles on top of them.
There is also a maintenance argument for putting the effort into specs rather than more tooling or teaching material. An increasing share of building runs through AI agents, and a spec is the deliverable that serves them best. Tutorials, SDKs, and example code are bound to one language and go stale as stacks evolve. A precise spec can be turned into working code in any stack, by a person or an agent, and only needs updating when the rules themselves change.
How new spec work flows
The repo hosts two kinds of specs of its own. Original specs for work that has no standards home anywhere (step 2), and profiles that say how existing external standards are combined for one Ethereum use case (step 3). Everything else is an indexed link to a spec that lives elsewhere. When new spec work shows up:
- Ask where it belongs. A mature standard that needs the whole ecosystem to implement it now goes straight into the ERC process. The same applies to other bodies, for example a proposal to change the credential format itself belongs in W3C, and improvements to anonymous routing might belong in the Tor spec process. The repo then just indexes such work, a link plus metadata, it doesn't rewrite it.
- If it has no home, host it. A private-read protocol or a verifiable RPC receipt has no existing standards body, so the spec is written here, starts as a draft, and matures to stable with conformance fixtures.
- If it combines existing standards, write a profile here. "Anonymous eligibility on Ethereum uses the W3C credential format plus this proof system plus this revocation method" is a spec no single external body would write, because it spans all of them. This is the main gap the repo fills.
- Graduate when ready. Wallet or contract interop work that is still taking shape can incubate here as a draft. Once it genuinely needs ecosystem-wide adoption, it moves into the ERC process and this repo keeps a pointer. The test is not whether something touches interop, it is whether it needs everyone to implement it yet.
Steps 2 and 3 produce documents nobody else will host. Step 1 makes sure work that belongs in an existing standards process goes there instead of being duplicated here.
The same flow as a decision tree:
Clear signing example (excerpts)
Builder resources (tools and infrastructure)
- Wallet rendering:
@ethereum-sourcify/clear-signing(TypeScript display engine),llbartekll/clear-signing(Rust display engine with mobile bindings) - Authoring and review: Ledger
erc7730(lint, format, generate), Cyfrinclearsig(translation, generation, and hashes) - Community tools:
hardhat-descriptor(Hardhat generation),@erc7730/sdk(transaction decoding),@txkit/tx-decoder(registry and ABI decoding) - Infrastructure: clear-signing registry (descriptor source)
ERCs
- 7730, 8176, 8213, 8283, 8317, etc.
Access layer specs (ideas only)
- Descriptor audit standard: defines what auditors must verify, which contract functions they must review, and which functions the descriptor must cover
- Offline clear-signing delivery protocol: defines how descriptors, attestations, and state proofs are packaged and sent to offline hardware wallets
Why the ethereum org
We checked every repo in the org. Outside the EIP/ERC standards process, every spec repo is protocol-layer or staking infrastructure (consensus, execution, JSON-RPC, devp2p, portal, builder, validator APIs, cryptography). For the access layer (wallet, identity, read/write UX), the org only has the ERC process. That process accepts drafts too, but only ones already shaped as a proposal for an ecosystem-wide standard, EIP-1's bar is a change that "defines a standard for multiple apps to use". Most access-layer spec work doesn't fit that shape at any maturity level.
The existing zkspecs entries show the difference. The Semaphore v4 spec defines the correct behavior of one protocol so builders can implement against it and auditors can check it, and the Anon Aadhaar spec pins down what one ZK circuit must prove. Nobody is asking every wallet to implement these, so they'll never be ERCs, yet builders still need them written down, reviewed, and versioned somewhere. If one day a slice of such a spec does need universal adoption, say Semaphore's on-chain interface, that slice graduates into an ERC. The rest, along with research-stage specs and profiles that combine external standards for one use case, needs a home. That home is what this repo provides.
Structure
Before settling on a structure we compared how 19 mature spec processes are set up, EIP, Rust RFC, Python PEP, TC39, IETF, W3C, the Linux Foundation family, and others. The design below follows the patterns that recur in the processes that have survived for years, and flags where we deviate.
The core of it, every spec lives in one numbered list under specs/, and that is the only place a spec exists. Which parts of the Access Layer a spec serves is a tag in its front matter, not a folder, so a spec that spans several needs no split and no duplicate.
The choices and their backing:
-
One flat list instead of per-category folders, because a spec routinely spans categories. Semaphore is read, write and prove at once. EIP, PEP, and Rust all work this way, category lives in metadata, the index gets generated, never hand-kept.
-
Categories are an optional
domainstag using the five Access Layer verbs, read, write, prove, delegate and exit. A tag and nothing more, no folders and no per-category pages. It groups specs and helps find a reviewer, and a spec carries as many as it needs. -
Folder names are
number-slug. The maintainer assigns the next free number when the spec is merged, which is what EIP, ENSIP and PEP do, and what this repo's own spec 1 already says. Numbers are never reused and never change. Taking the number from the pull request instead, as Rust and Solana do, removes the assignment step but leaves permanent gaps, and here would start the next spec at roughly 34 while the existing five are 1 to 5. -
Every spec's front matter carries a
typenaming what kind of spec it is. The starting values:- protocol, the complete behavior of one system (a private-read protocol)
- interface, an API or message format
- profile, how existing standards are combined for one use case (clear signing spans three ERCs)
- schema, an exact data shape (a descriptor schema that test scripts validate against)
- process, the repo's own working rules
Intake issues get a matching GitHub label so the incoming queue is filterable by type. Conformance test vectors are not a type, they sit in each spec's
fixtures/folder. New values get added when the first spec of a new kind arrives, not in advance. -
Every spec carries a
statusfield with its lifecycle stage, draft, review, stable, deprecated, plus three side statuses, stagnant (6 months without activity, can be revived), withdrawn (given up, terminal), and living (meant to change continually and never freeze, used for process docs and for indexed externals that track their upstream). A spec is merged as soon as it is readable and in scope, and that merge puts it atdraft, so there is no pre-merge status. Promotion to stable requires evidence, passing conformance fixtures or two independent interoperating implementations (the W3C/IETF gate). -
Versioning follows the numbering. A clarifying edit changes the spec in place. A behavior change, say Semaphore v4 to v5, is a new numbered spec. The old one flips to
status: deprecatedwithreplaced_bypointing at the new number, and the new one carriesreplaces. Permalinks to the old spec keep working. -
Conformance fixtures sit next to each spec. Ethereum itself moved to this in July 2026 when execution-spec-tests was merged into execution-specs.
-
Every entry is tagged with the role this repo plays for it, so external standards we depend on (W3C Verifiable Credentials, Tor, EIPs) are visible without pretending they live here. Hosted means the spec lives here and this repo is its source of truth. Mirrored means a full copy is kept here (for fragile upstreams like HackMDs or papers, or frozen audit snapshots) while the upstream stays authoritative. Indexed means only a stub with a link, the spec is read upstream. (Things that might become specs, like a promising HackMD, don't get an entry, they're tracked as intake issues until someone converts them.) This tagging is experimental, no peer process does it, CAIP's namespaces repo is the closest precedent.
That gives this layout:
access-layer-specs/
README.md
registry.yaml # generated from spec front matter
process/ # how the repo works (like EIP-1): lifecycle stages,
# spec template, issue-vs-PR intake rules, governance
specs/
0001-private-read-protocol/
README.md # spec + front matter (status, domains, relationships)
fixtures/ # conformance test vectors, co-located with the spec
A normal hosted spec looks like this, front matter on top, then the spec text itself:
# specs/12-verifiable-rpc-receipt/README.md
---
id: 12
title: Verifiable RPC Receipt
shortname: VERIFIABLE-RPC-RECEIPT
role: hosted
type: interface
status: review
domains: [read, prove]
editor: Someone <someone@example.org>
depends_on: [11]
---
## Motivation
...
## Specification
A receipt is a signed JSON object with the fields ...
## Security Considerations
...Its fixtures, when it has them, sit next to it, specs/12-verifiable-rpc-receipt/fixtures/valid-receipt.json, invalid-missing-proof.json, and so on.
The README index table and a machine-readable registry.yaml are generated from that front matter, so the list of specs is never hand-kept and cannot drift from the specs themselves.
An indexed external standard gets the same numbered folder as a hosted spec, just with a stub README instead of a full spec. That keeps one uniform system, the registry and views are generated from the same front matter, and hosted specs can reference it in depends_on. For example:
# specs/11-w3c-verifiable-credentials/README.md
---
id: 11
title: W3C Verifiable Credentials Data Model 2.0
shortname: W3C-VERIFIABLE-CREDENTIALS
role: indexed
type: schema
status: living
index_reason: dependency
upstream: https://www.w3.org/TR/vc-data-model-2.0/
domains: [prove]
editor: Someone <someone@example.org>
---
Credential format used by Access Layer eligibility and zkID work.
The spec is maintained by W3C, this entry only records how it is
used here. See specs 14 (eligibility profile) and 17 (revocation).No fixtures, no lifecycle progression, a couple of sentences on why it matters and which hosted specs build on it.
The stub is not a copy of the external spec, readers still go upstream to read it. Compared to a bare URL in each spec, it gives hosted specs a stable ID to reference in depends_on, makes "what here builds on W3C VC?" an exact reverse lookup instead of URL matching, and holds the Ethereum-side context (targeted version, fields used, caveats) in one place, so a new upstream version means updating one stub rather than every spec that links it. To keep this from growing into a catalog of every interesting spec in the world, every indexed entry names its reason in index_reason, which takes exactly two values. dependency means a hosted spec lists it in depends_on, the usual case for an external standard. moved means the text was hosted here and now lives in another standards body or repository, and it requires upstream. A spec that graduated out of the repo is the second case. The rule is mechanically checkable, CI rejects any indexed entry whose condition does not hold, and free text is not accepted, because a reason nobody can check is not a rule. Anything merely worth mentioning stays a plain hyperlink in a spec's prose, and transitive dependencies of external specs stay out entirely.
The same mechanism handles graduation. A spec that moves into the ERC process keeps its folder and number, its front matter just flips to point at the ERC as the new source of truth, so existing links and depends_on references stay valid:
# specs/7-session-key-scopes/README.md
---
id: 7
title: Session Key Scopes
shortname: SESSION-KEY-SCOPES
role: indexed # was: hosted
type: interface
status: stable # unchanged, the spec is still maintained
upstream: https://ercs.ethereum.org/ERCS/erc-9999
replaced_by: ERC-9999
index_reason: moved
domains: [write, delegate]
editor: Someone <someone@example.org>
---
Graduated to ERC-9999. The ERC is now canonical. This entry stays
so existing references and links keep working.
An indexed entry has to earn its place, or the repo slowly becomes a
catalogue of every interesting spec in the world. `index_reason` takes
exactly two values and CI checks the matching condition. `dependency`
means a hosted spec lists it in `depends_on`. `moved` means the text was
hosted here and now lives elsewhere, and requires `upstream`. A
graduated spec is the second case, which is why nothing here has to
depend on it. A sentence of your own is not a reason.Governance
Licence, settled 15 September 2026. Everything in the repo is CC0, the same as the EIP and ERC repos. Every spec ends with the line "Copyright and related rights waived via CC0", and that line is the whole contributor agreement, so there is nothing to sign and nothing to retrofit. Specs 2 to 5 already carried it. A spec that graduates into an ERC needs no relicensing, which the ERC process requires. CC0 covers copyright only, so implementers get no patent promise, and the EIP process lives with the same gap. The Linux Foundation Community Specification template was considered and dropped, it solves a problem the EIP family does not have.
Two things are still open:
- A written approval rule. What counts as consensus for merging or promoting a spec.
- A named tie-breaker. Who decides when reviewers disagree, so a dispute can't freeze the repo.
Migration path
- Add process docs, the spec template, and the generation script. Open as process: add repo process docs and rewrite README #30.
- Put the five existing specs on the schema and rename their folders to
number-slug, keeping their numbers and their history. Open as specs: normalise front matter and rename folders to number and shortname #31. - Generate
registry.yamland the README table, with CI rejecting stale output and invalid front matter. Open as ci: generate registry.yaml and the README table from front matter #32. - Settle which process document governs the repo, since 1/COSS defines a different lifecycle. Done, folded into specs: normalise front matter and rename folders to number and shortname #31, which retires 1/COSS with
replaced_by: process/and keeps its text. - Rename the repo to
access-layer-specs. Done, 14 September 2026. Old links redirect. - Open intake issues for the next likely specs (TLSNotary, PIR/private reads, private transfer research, zkID/OpenAC-related specs, revocation notes, POD/VC interface work).
- Convert the highest-priority ones into hosted specs once ownership and review paths are clear.
The proof-of-personhood spec was going to come back here from zkID, opened as #34 and closed on 15 September 2026. Its editor and its implementation are both in zkID, so hosting a copy would mean two drifting texts. It stays indexed instead.
Activity
andyguzmaneth commentedon Aug 20, 2026
phipsae commentedon Aug 21, 2026
@andyguzmaneth thanks so much for the questions! Here are my answers:
Yes, that was the idea, the four domains are the four domain teams (so that every team can oversee their specs). But you're pointing at the right question, whether they're generic enough to survive the teams changing, and whether they actually cover everything or we're missing something. The same applies to the name, is access-layer-specs right or should we draw it wider so the repo survives those changes too.
Maybe we can go with the five verbs from the strategy doc instead: read, write, prove, delegate, exit?
My thought is that every tool can link to the specs itself (thats on the tool maintainer), or in our builders resources would like also link to the spec, but not the other way around. I want prevent that specs catalogue to recommend tools. Tools are getting outdated, so we need to update this here as well like we do in builders resources
I looked up how other orgs name this, and most use profile (IETF, W3C, etc.). So I would go with profile, but totally open for pattern as well.
Not covered yet. I would add a new spec, and set the old to
status: deprecatedwithreplaced_byporinting at the new number and in the new one we can havereplaces. What do you think?andyguzmaneth commentedon Aug 23, 2026
phipsae commentedon Sep 14, 2026
Implementation is up as four pull requests, stacked in this order.
process/docs, spec template, README rewritenumber-slugregistry.yamland the README table generated from front matter, with CIprocess/I have updated the issue body above to match. Three things changed from what it said before.
Categories are a front-matter tag and nothing else. No
domains/folder, no generated category pages. Meyanis95 raised on 14 September that Semaphore is write, read and prove at once and preferred shipping without categories. A tag holds that without forcing a split, and #31 tags Semaphore with all three. Values are the five verbs from the strategy doc, which andyguzmaneth agreed to above.Spec numbers are assigned on merge, not taken from the pull request number. I had proposed the pull request number, following Rust and Solana. Three reasons to switch. EIP, ENSIP and PEP all assign at merge. This repo's own spec 1 already says specs carry "an incremental number", so the other rule would have contradicted a spec sitting in the repo. And the counter here is past 29, so the next spec would have landed at roughly 34 next to the existing 1 to 5, with nothing explaining the gap.
Ask 3 is now just reviewers, not one per domain, since there are no domains to review per.
Two things still need an answer from this thread, both unchanged since 23 August.
The licence question in
process/governance.mdis mine to chase with EF legal.The rename to
access-layer-specshappens after these merge, not before.docs: repo renamed to access-layer-specs
ci: stop the guardrail blocking graduation, and validate replaced_by
ci: stop the guardrail blocking graduation, and validate replaced_by
oskarth commentedon Sep 15, 2026
Awesome to see you guys shipping this @phipsae!
process: add repo process docs and rewrite README (#30)