-
Notifications
You must be signed in to change notification settings - Fork 2
rfc implementation
Pre-Alpha. This page describes behavior that may change.
Ze implements a lot of RFCs. Implementing an RFC in Ze is not a matter of "read the RFC and write the code". It is a matter of tracking which RFCs the project depends on, recording what has been implemented and what has not, and making sure the implementation stays aligned with the RFC text over time. This page covers how that process works.
The heart of the process is a machine-enforced gate. Every RFC 2119 MUST-level requirement of an enrolled RFC must be either proven by tests or explicitly annotated with a reason; make ze-rfc-check fails the build otherwise. As of this writing 166 documents are enrolled (163 RFCs and 3 drafts), covering 2720 gated MUST-level requirements across 173 summaries, with none outstanding. The gate runs as part of make ze-verify, so an implementation cannot quietly drift away from the RFC text without a red build.
The canonical rule file is ai/rules/rfc-compliance.md in the repo, and the enrolment/gate mechanics live in ai/skills/ze-rfc.md. This page summarises them.
RFCs tracked by Ze live under main/rfc/ in the repo.
main/rfc/
├── README.md # Human index of core BGP RFCs
├── enrolled.txt # The gate's scope: RFCs whose MUSTs are machine-enforced
├── short/ # Per-RFC requirement summaries (each MUST has a stable id)
├── drafts/ # Draft RFC tracking
├── full/ # Full RFC text references
├── audit/ # Letter-and-spirit audit verdicts per requirement
└── ...
The short extracts are compressed versions of the RFC sections that the code actually depends on, so cross-checking a claim against the RFC does not require re-reading the full document. Each MUST-level line in rfc/short/<rfc>.md carries a permanent requirement id of the form RFC<n>-<section>-<ordinal> (for example RFC7606-7.1-1). That id is the contract a test binds to.
rfc/enrolled.txt is the scope of the enforced gate: one row per document, and the file grows only. Un-enrolling an RFC fails the gate, so an obligation that was once proven cannot quietly stop being proven. As of this writing 163 RFCs and 3 drafts are enrolled.
The public, plain-English status ledger is separate: docs/features/rfc-status.md lists every RFC Ze implements, partially implements, or intentionally does not, with the source anchors and the named gaps.
Every RFC implementation in Ze follows the same five steps.
-
Spec. Before any code is written, the maintainer drafts a spec under
plan/that names the RFC, lists the sections being implemented, and defines the acceptance criteria. The spec explicitly calls out what is out of scope. Specs are written fromplan/TEMPLATE.md. -
Short extract. If the RFC has load-bearing sections that the code will reference (wire formats, validation rules, state machine transitions), the sections are extracted into
rfc/short/<rfc>.md. The extract is a compression, not a summary: it preserves the exact text of the parts that matter. -
TDD implementation, with tagged tests. The tests come first, and every test that proves a MUST-level obligation is bound to that obligation by a tag:
// RFC requirement: RFC7606-7.1-1 positive(and a matchingnegative). Binding the test to the requirement id is what lets the gate prove the link exists and catch a later regression. -
Code. The implementation goes in the appropriate component. Every non-trivial decision cites the RFC section in the code comment:
// RFC 4271 §9.1.2.2: route selectionand so on. This is what lets reviewers verify the code against the RFC without reading both cover to cover. -
Enrol in
rfc/enrolled.txt. Once the tests exist, the RFC is added torfc/enrolled.txtsomake ze-rfc-checkstarts enforcing it. Writing a summary does not enrol an RFC; enrolment is a deliberate, separate step taken only once every MUST is proven or annotated. The file grows only: an obligation that was once proven cannot quietly stop being proven.
The gate, make ze-rfc-check, is the mechanism that keeps an implementation honest over time. For every RFC listed in rfc/enrolled.txt, it requires that each MUST-level requirement in rfc/short/<rfc>.md is one of:
- Proven both ways. A positive AND a negative test, each tagged with the requirement id. Both polarities are required because a one-sided test passes on a blanket accept or a blanket reject.
-
Annotated with a reason.
{gap: why; ref}for a deliberate divergence,{not-applicable: why}for an obligation Ze cannot play (for example a role it never takes), or{single-polarity: positive|negative; why}when a requirement is genuinely testable only one way. A bare annotation is rejected; every one carries its reason.
The gate runs inside make ze-verify, so a change that breaks a proven obligation, or an enrolled RFC that loses a required test, fails the build.
The gate reads the working tree to judge coverage, and a tree cannot tell "never proven" from "stopped being proven". Four ratchets compare against HEAD so coverage can only go up:
| Ratchet | Fires when |
|---|---|
| Enrolment is monotonic | An RFC whose MUSTs were gated stops being gated. |
| Proof is monotonic | A requirement loses a polarity it had at HEAD. Annotating it {gap} is not an escape: that is the move being blocked. |
| Requirements do not vanish | A requirement id of an enrolled RFC disappears from its summary. |
| Adding an RFC adds checking | A summary that is new since HEAD declares gated MUSTs but is not enrolled, fails to parse, or captures nothing while the full text has MUST-level keywords. |
The third exists because deleting a checklist line would otherwise be the cheapest route from red to green, cheaper than {gap}, which costs a public disclosure row. That would pressure contributors to hide obligations rather than declare them. Correcting a misquote is still allowed: edit the text under the same id.
What none of this catches is a tagged test whose assertions are weakened in place while it keeps its tag. That is what the audit below is for.
Two companion tools sit alongside it:
-
ai/RFC-REQUIREMENTS.mdis the generated ledger of requirement to enforcing test, and the tracked backlog: un-enrolled RFCs are listed there marked "not enrolled", so the remainder is visible, not hidden. -
/ze-rfc-auditreads the RFC text and each tagged test and judges whether the test would actually fail if the code stopped complying. The gate proves a link exists; the audit judges whether the link is meaningful, andmake ze-rfc-checkre-stales a verdict when the requirement text or a tagged test changes.
A tagged test is the requirement: once a test carries an RFC requirement: tag, you fix the code to pass, never the test. Changing a tagged test needs explicit recorded approval, and a hook blocks the edit otherwise.
Enrolment is the closest thing Ze has to a binary "complete": an enrolled RFC has every MUST-level obligation either proven or reasoned. That is a stronger and more honest bar than "we wrote the code", because the divergences are named in the summary and surfaced in the public ledger rather than left implicit.
The plain-English status is a separate, coarser view. docs/features/rfc-status.md records each RFC as Supported, Experimental, Partial, Unsupported, or Future, with the named gaps. A Partial there often means "enrolled, with the specific MUSTs it does not meet annotated as {gap}", not "untested".
Deviations are not a small number of cases, and they are not kept in a hand-written list. They live where the requirement lives: as a {gap: ...} annotation on the specific checklist line in rfc/short/rfcNNNN.md, naming the producing function and file:line that fails to meet it. There are 539 such annotations across 84 of the 166 enrolled RFCs.
That is the honest shape of it. An enrolled RFC is one where every MUST-level obligation has been accounted for, not one where every obligation is met. A Partial in docs/features/rfc-status.md usually means "enrolled, with the specific MUSTs it does not meet annotated as gaps".
Three real examples from rfc/short/rfc4271.md:
| Requirement | Gap |
|---|---|
RFC4271-5-3 |
An unrecognized transitive optional attribute is not re-advertised with the Partial bit set. The stored flags octet is replayed verbatim, so one received with Partial=0 goes back out with Partial=0. |
RFC4271-4.3-5 |
A prefix appearing in both WITHDRAWN and NLRI of one UPDATE ends up removed rather than installed: the announce is applied first and the withdrawal second. |
RFC4271-5.1.3-2 |
No self-next-hop check on the BGP install path, so a route whose NEXT_HOP is the local speaker's own address reaches the Loc-RIB. The FIB resolver rejects it downstream, which does not remove it from the Loc-RIB. |
A gap annotation is a commitment to a file:line, so it rots loudly rather than quietly: the ratchets described above refuse to let a requirement lose a polarity it once had, and refuse to let a requirement line be deleted rather than fixed.
Separately, docs/exabgp/exabgp-differences.md records deliberate ExaBGP-vs-Ze differences, which are mostly API surface rather than protocol. It currently holds one live entry (neighbor qualifier syntax for multi-session targeting: ExaBGP supports per-session qualifiers on the neighbor keyword, Ze applies commands to every session matching the peer IP). Its attribute-ordering entry is resolved: Ze now emits attributes in ascending type-code order in all three builders, the same as ExaBGP.
When you need to verify that a piece of Ze code matches what the RFC says, the process is:
- Find the short extract under
rfc/short/<rfc>.md. - Cross-reference with the code comments that cite the section.
- Read the tests that target the section.
- If anything is missing or unclear, read the full RFC section.
The short extract plus the code comments plus the tests should be enough for most reviews. The full RFC is a fallback.
Step zero of implementing a new RFC in Ze is to write its requirement summary under rfc/short/<rfc>.md, giving every MUST-level line a permanent id, before any spec is written. This makes the RFC's obligations concrete and forces the question "is this RFC actually worth implementing in Ze" to be answered against a real list rather than a vibe. Until the tests exist the RFC stays un-enrolled, and appears in ai/RFC-REQUIREMENTS.md marked "not enrolled": tracked as backlog, not silently dropped.
The gate can only check requirements a summary actually lists, so an obligation that was never extracted is invisible to it. Enrolling a summary therefore requires a completeness pass first, and two signals matter most:
-
A
{not-applicable}whose reason is "Ze has no X at all." That admission is often itself the violation of a separate MUST requiring X to exist. RFC 4271 section 5.1.4 requires a mechanism that allows MULTI_EXIT_DISC to be removed; that MUST was unextracted, and two other requirements cited its absence as their own exemption. - A section whose siblings are enumerated but one clause is not. RFC 8666 section 5's "MUST be ignored on reception" was omitted while sections 6, 7.1 and 7.2 each carried it. That is an enumeration hole, not a style choice.
The gate's silence is not conformance. An unextracted obligation is still an obligation.
Claude Code has two project-specific slash commands for this workflow. /ze-rfc generates a requirement summary from an RFC and allocates the permanent requirement ids; it is a planning aid, not a code generator. /ze-rfc-audit performs the letter-and-spirit audit described above, reading the RFC and the tagged tests to judge whether the tests really enforce the requirement.
- Contributing for the spec-first contribution workflow.
- Coding standards for the TDD rule.
- Claude Code for the slash commands.
-
main/rfc/enrolled.txt,main/ai/RFC-REQUIREMENTS.md, andmain/ai/rules/rfc-compliance.mdfor the canonical gate and ledger.
Unreviewed draft. This wiki was authored in bulk and has not been reviewed. File corrections on the issue tracker.
- Overview
- YANG Model
- Editor Workflow
- Archive and Rollback
- System
- Interfaces
- VRRP
- BFD
- FIB
- OSPF
- IS-IS
- MPLS / LDP / RSVP-TE
- RSVP-TE
- SRv6
- Static Routes
- Policy Routing
- Firewall
- Traffic Control
- Class of Service
- L2TP/PPP
- PPPoE
- VPP Data Plane
- RPKI
- IPsec VPN
- TACACS+ AAA
- RADIUS AAA
- AS112 DNS
- Authorization
- Fleet
- BGP
- Starting and Stopping
- Show Commands
- Monitoring
- Flow Export
- DDoS Mitigation
- Anomaly Detection
- Health Checks
- Audit Trail
- Production Diagnostics
- Logging
- Operational Reports
- Healthcheck
- Self-Update
- Zero-Touch Provisioning
- MRT Analysis
- Upgrade and Restart
- Storage
- Policy
- Core
- Resilience
- Validation
- Capabilities
- Address Families
- Protocol
- Subsystems
- Infrastructure
- Route Server at an IXP
- Transit Edge with RPKI
- Public Looking Glass
- ExaBGP Migration Walkthrough
- FlowSpec Injection
- Chaos-Tested Peering
- AS Path Topology