CIP-16 v2
Versioning. This is v2 of CIP-16. v1 is the canonical documentcip-16-custom-domains.md(preserved verbatim as Part I). v2 = v1 + the alignment revision (Part II) + the cross-cutting conventions (Part III). Conflict rule: Part II is canonical wherever it contradicts Part I. Summary of v2 changes
VerificationMode::MajorityVotefor DNS verification (notDeterministic). v1 §9.6 chose Deterministic, which requires byte-identical output + TEE — wrong for non-deterministic DNS. MajorityVote is already implemented (runner/src/types.rs:215).- Two new
VerifierCheckvariants —DnsTxtRecordMatchandDnsCnameMatch(per CIP-2 v2 Part II §2). Each runner queriesmin_resolversindependent recursive resolvers.- Explicit
RouteRegistration → DomainBindingmigration rule with field defaults for legacy CIP-14 records.complete_attach_externalenforced at protocol layer. NewSystemInstruction::ExternalDomainCallbackallowlisted toRESULT_VERIFIER=0x03.EXTERNAL_REVERIFY_FEEcharged from binding owner each reverify firing; insufficient balance →SUSPENDED.verified_fqdninjected intoHttpRequestEnvelopebyGatewayRegistry. Actors trust this rather than the attacker-controllablehostheader.- HTTP status codes corrected —
SUSPENDED→503(not421 Misdirected Request).CANONICAL_EDGE_HOSTNAMEmode made explicit — anycast (default) or SRV (optional).- Centralization risks enumerated — ACME control plane, anycast edge, first-party TLD authority, governance-pinned DNS verifier executor.
Part I — v1 Specification (verbatim from cip-16-custom-domains.md)
Status: Draft
Type: Standards Track
Category: Core
Created: 2026-03-08
Requires: CIP-14 (DNS-Addressable Actors), CIP-2 (Off-Chain Compute), CIP-3 (Dual-Metered Gas), CIP-5 (Timers)
1. Abstract
This proposal extends DNS-Addressable Actors (CIP-14) with two additional naming modes:- First-party TLD names under protocol-controlled zones such as
.cowand.cowboy - External custom domains such as
api.example.com, attached to an actor through DNS-based verification
- How the existing Route Registry is extended to manage domain classes beyond
*.cowboy.network - Registration and renewal rules for first-party TLD labels (for example,
alice.cow) - DNS-based control verification for externally attached domains
- Canonical DNS and TLS onboarding flows for external domains
- Periodic reverification and suspension rules for externally attached domains
- Resolution precedence and compatibility with CIP-14 routing semantics
.cow and/or .cowboy. The business and ICANN mechanics of obtaining and operating those TLDs are outside protocol scope; this CIP defines only the protocol behavior once that control exists.
2. Motivation
CIP-14 deliberately launched with a subdomain-first model undercowboy.network to keep ingress simple. That is the right v1 for internet-addressable actors, but it leaves two important gaps:
- Native protocol naming: If Cowboy controls
.cowor.cowboy, actors should be reachable at second-level names likealice.cowrather than only atalice.cowboy.network. - Brand and migration support: Teams and users already own DNS names such as
app.example.com. They should be able to attach those domains to actors without forcing users into a Cowboy-only namespace.
alice.cow, alice.cowboy, alice.cowboy.network, or api.example.com, the result should be the same: the Gateway resolves a verified route and dispatches to the actor defined by the Route Registry.
3. Design Goals
- Extend CIP-14 without changing its query/command execution model
- Preserve ordinary DNS and browser compatibility; no alternative roots
- Use a single authoritative on-chain registry model for all actor-bound names
- Make external-domain attachment verifiable and revocable through DNS state
- Separate protocol-owned namespace registration from external-domain attachment
- Keep wildcard and advanced delegation out of v1 unless required
- Make periodic reverification explicit so stale or hijacked external domains do not remain bound indefinitely
4. Non-goals
- Defining the legal or registrar-business process for obtaining
.cowor.cowboy - Tokenizing domains as transferable RWAs or DeFi assets
- Supporting wildcard external domains in v1
- Supporting arbitrary alternative DNS roots or Handshake-style name resolution
- Defining static asset serving; that remains follow-on work on top of CIP-9 public volumes
- Defining payment gating or stream bridging; those remain follow-on CIPs
5. Definitions
- First-party TLD: A DNS zone operated by Cowboy at the registry/authoritative-DNS level, such as
.cowor.cowboy - Second-level label: The label registered directly under a first-party TLD, such as
aliceinalice.cow - External domain: A domain or subdomain not under a Cowboy-controlled TLD, such as
api.example.com - Domain attachment: Binding an external domain to an actor after proving control of the DNS name
- Verification challenge: A protocol-generated token that must appear in DNS to prove external-domain control
- Canonical edge target: The hostname or DNS target to which external domains point for Cowboy Gateway ingress
- Suspended binding: A domain binding that remains recorded on-chain but is not served because verification or DNS requirements are no longer satisfied
6. Domain Classes
6.1 First-Party TLD Names
When Cowboy controls.cow and/or .cowboy, the Route Registry becomes the authoritative registration source for second-level labels:
subdomain_policy model introduced in CIP-14.
6.2 External Custom Domains
External domains are attached, not minted. In v1, this CIP supports exact FQDN attachment only:*.example.com) is deferred.
6.3 Compatibility With cowboy.network
The existing *.cowboy.network namespace from CIP-14 remains fully valid. This CIP adds new namespace classes; it does not replace the original one.
7. Route Registry Extension
This CIP extends the existing Route Registry system actor from CIP-14 rather than creating a second naming registry.7.1 Extended Binding Record
The Route Registry record is generalized as:7.2 Normalization Rules
- All FQDNs MUST be normalized to lowercase ASCII punycode before storage
- Trailing dots MUST be removed before normalization
- Unicode input MUST be rejected unless it round-trips to a valid ASCII punycode name
- Comparisons are exact after normalization
7.3 Resolution Precedence
Resolution is exact-FQDN first:- exact external binding
- exact first-party TLD binding
- exact
cowboy.networkbinding - parent subtree handling via
subdomain_policy
8. First-Party TLD Registration
8.1 Supported TLDs
This CIP defines protocol behavior for the following first-party zones:.cow.cowboy
8.2 Registration
The Route Registry exposes:
Requirements:
- The target actor holds
ingress.http - The label is available or has expired past grace/auction
- Registration fee is paid in
CBY - The caller is the actor or its controlling owner account
8.3 Economics
First-party TLD names use the same scarcity model as CIP-14 names:- shorter labels pay higher annual fees
- renewals extend from current expiry
- expired labels enter a grace period followed by a release auction
.cow and .cowboy independently.
8.4 Reserved Labels
Governance reserves protocol-critical labels such as:wwwapidnsgatewayrelaynodeadminsystem
9. External Domain Attachment
9.1 Overview
An external domain is attached in three phases:- Begin attachment on-chain
- Prove control through DNS
- Activate binding after off-chain verification and certificate readiness
9.2 Attachment API
The Route Registry exposes:
Requirements for
begin_attach_external:
- The normalized
fqdnis not under a Cowboy-controlled namespace (*.cowboy.network,.cow, or.cowboy) - The target actor holds
ingress.http - The caller is the actor or its controlling owner account
- The exact normalized
fqdndoes not already have anACTIVE,PENDING, orSUSPENDEDbinding fqdnis an exact host name, not a wildcard pattern
9.3 DNS Control Proof (v1)
The v1 verification method is TXT challenge. Whenbegin_attach_external(fqdn, actor_address) is called, the Route Registry generates:
CHALLENGE_EXPIRY_BLOCKS after begin_attach_external.
9.4 Canonical Edge Target
In addition to the TXT challenge, the external domain MUST point to the Cowboy edge. For v1:- subdomains MUST use
CNAMEto the canonical edge hostname - apex domains MAY use
ALIAS/ANAMEor equivalent flattening if the DNS provider supports it
.cow or .cowboy is live.
9.5 TLS Onboarding
External domains in v1 use gateway-managed TLS with delegated DNS-01: The owner MUST publish:9.6 Verification Execution
External verification uses CIP-2 off-chain jobs:begin_attach_externalemitsExternalDomainVerificationRequested- The protocol dispatches a verification task to multiple verifiers
- Verifiers confirm:
- the TXT challenge matches exactly
- the domain points at the canonical edge target
- ACME delegation exists
- A verifier callback invokes
complete_attach_external - The binding becomes
ACTIVEonly after verification succeeds
9.7 Status Transitions
9.8 Reverification
External domains MUST be reverified periodically.next_reverify_atis set when activation succeeds- a CIP-5 timer triggers reverification every
EXTERNAL_REVERIFY_INTERVAL - if TXT proof, edge target, or ACME delegation is missing at reverification time, the binding becomes
SUSPENDED
10. Gateway Behavior
10.1 Resolution
Gateways resolve incoming hostnames by querying the Route Registry with the fully normalized FQDN. For an external domain, the Gateway MUST confirm:- binding status is
ACTIVE - the binding is not past
next_reverify_at - TLS certificate is present and valid
10.2 Serving Policy
If an external binding isPENDING, SUSPENDED, EXPIRED, or DETACHED, Gateways MUST NOT dispatch to the actor.
Recommended responses:
PENDING:503 Service UnavailableSUSPENDED:421 Misdirected Requestor503 Service Unavailable(Part II §7.2 is canonical and narrows this to503)EXPIRED:404 Not FoundDETACHED:404 Not Found
10.3 First-Party TLD DNS
For.cow and .cowboy, the Cowboy-controlled DNS authority serves records directly from the Route Registry namespace. No TXT proof or external ACME delegation is required for first-party TLD labels.
11. Security Considerations
11.1 Domain Hijack and Stale Control
External-domain control can change off-chain without any on-chain transaction. Periodic reverification is mandatory to reduce stale bindings.11.2 DNS Normalization
All domain comparisons use normalized punycode ASCII. Gateways and the Route Registry MUST reject mismatched or non-normalized names.11.3 Certificate Issuance Abuse
Gateways MUST NOT attempt certificate issuance for an external domain until:- TXT challenge is valid
- edge target is correct
- ACME delegation is present
- the Route Registry status is
PENDINGfor the same nonce
11.4 External DNS Target Drift
If an external domain stops pointing at the Cowboy edge, the binding becomes suspended at the next verification cycle. Gateways MUST NOT continue serving stale attachments indefinitely.11.5 Official TLD Concentration Risk
First-party TLD operation introduces operational trust in the entity or governance process controlling.cow / .cowboy. This is unavoidable for ICANN-root DNS and is a known centralization point outside the consensus protocol.
12. Protocol Constants
13. Future Work
The following are explicitly deferred:- wildcard external domain attachment
- DNSSEC-native verification path
- delegated registrars or reseller model for
.cow/.cowboy - bring-your-own certificates
- domain tokenization / marketplace mechanics
- unified public asset hosting manifests across CIP-9 and CIP-14
14. Backwards Compatibility
This CIP is additive to CIP-14:- Existing
*.cowboy.networknames remain valid and unchanged - Existing Route Registry records remain valid
- Gateways that do not implement CIP-16 simply cannot serve first-party TLD or external attached domains
- No changes are required to the query/command routing model from CIP-14
15. Rationale
15.1 Why Reuse the Route Registry
The route decision is the same regardless of namespace: resolve normalized FQDN to actor and dispatch through the CIP-14 ingress path. Splitting naming across multiple registries would create ambiguous authority and duplicate lifecycle logic.15.2 Why TXT Challenge in v1
TXT-based proof is operationally simple, widely supported, and already familiar from certificate issuance flows. It is not fully trustless, but it fits Cowboy’s off-chain verification model and keeps the first custom-domain release practical.15.3 Why Exact External FQDNs Only
Wildcard attachments dramatically increase risk and verification complexity. Exact-FQDN attachment is enough for most APIs and websites, and leaves room for a future wildcard CIP once the Gateway and verification planes are battle-tested.15.4 Why Assume .cow / .cowboy
If Cowboy controls the TLD, there is no reason to force all protocol-native names under cowboy.network forever. Supporting second-level labels under .cow / .cowboy is the natural extension of CIP-14’s naming model.
Part II — v2 Revision (canonical; verbatim from former cip-16-aligned.md)
Status: Draft (alignment revision; non-modifying companion to
cip-16-custom-domains.md)
Type: Standards Track
Category: Core
Created: 2026-04-21
Companion to: cip-16-custom-domains.md
Reads with: Part III of this document, cip-14-dns-addressable-actors-v2.md (Part II)0. What this document is
A code-aligned revision of CIP-16. The original specifies external-domain verification via “CIP-2 multi-verifier consensus” but reusesVerificationMode::Deterministic (which requires byte-identical output and TEE — wrong for non-deterministic DNS resolution). It leaves complete_attach_external as “system-only” without spelling out which system actor enforces the rule. It silently replaces CIP-14’s RouteRegistration schema. It treats CANONICAL_EDGE_HOSTNAME as a single hostname without addressing multi-Gateway deployments. It chooses HTTP 421 Misdirected Request for SUSPENDED bindings (which invites the wrong client behavior). And it leaves external-domain reverification fees unspecified, externalizing verifier compute cost onto the protocol indefinitely.
This document picks concrete answers for each.
1. Preconditions
On-chain status (2026-07). AMEND 2-A/2-B are already deployed:VerifierCheck::DnsTxtRecordMatchandDnsCnameMatchexist in the runner enum and are handled by node execution (node/execution/src/runner/verifier.rs,dispatcher.rs;node/runner/src/executors/dns.rs). They are no longer pending amendments; the rows above are retained for provenance. | §4 |cip-14-dns-addressable-actors-v2.md(Part II) | Route Registry exists at0x0Ewith the schema in §3 below |
2. Scope
Same as original §1: protocol-owned.cow / .cowboy second-level labels and externally attached FQDNs (exact-match in v1; wildcards deferred). Execution model is unchanged from CIP-14-aligned (read-handler RPC + system-mediated dispatch).
3. Route Registry record (extends CIP-14-aligned §4.1)
The original CIP-16 §7.1 silently replaced CIP-14’sRouteRegistration. CIP-16-aligned makes the migration explicit and enumerates default values for legacy records.
The deployed on-chain struct is node/types/src/domain.rs::DomainBinding — 9 fields; enums serialize as snake_case strings, not u8:
⚠️ Errata (2026-07). The above matches deployed code and supersedes the earlier field list. Key drifts from prior revisions: (1)namespace_kindis{Actor, Gateway}(the routing-target kind), not{COWBOY_NETWORK, FIRST_PARTY_TLD, EXTERNAL}— that network axis istld_kind(see §12). (2)namespace_kind/status/tld_kind/verification_methodare enums serialized as snake_case strings, notu8. (3) Ownership and term (owner,registered_at,expires_at) are NOT onDomainBinding— they live in a separateTldLabelRecord { owner, registered_at, expires_at }keyedcip16:label:<fqdn>(domain.rs). (4) There is noactor_addressfield (routing goes throughdns_target) and nosubdomain_policyfield on the binding.
3.1 Migration from CIP-14-aligned (one-time at upgrade boundary)
ExistingRouteRegistration records become DomainBinding with these defaults:
fqdn is computed from the existing name field as name || ".cowboy.network". No records are deleted or moved. Migration runs once at the protocol upgrade boundary (system actor at 0x0E reads its own state and writes back the augmented schema in a single block).
3.2 Resolution precedence
Exact normalized FQDN lookup. The original §7.3 ranking (“exact external > first-party TLD > cowboy.network”) is dropped because namespaces are disjoint — an externalapi.example.com cannot collide with a cowboy.network subdomain. One key, one lookup.
3.3 Normalization rules
Unchanged from original §7.2 (lowercase ASCII punycode, trailing dots stripped, exact comparison).4. First-party TLD registration
Same as original §8 except:- Method names match CIP-14-aligned style:
register_tld_label,renew_tld_label,transfer_tld_label,set_actor. - Fee splits route through
system:cip16_registry_settlement(Part III of this document §6) — same configuration thecowboy.networkregistrations use, optionally with TLD-specific overrides viatarget_pool: REGISTRY_TLD_<COW|COWBOY>discriminants. - Reserved labels (
www,api,dns,gateway,relay,node,admin,system) apply per-TLD, governance-managed.
5. External domain attachment
5.1 Three-phase flow
Same shape as original §9.1: begin → prove → activate. Implementation differences below.5.2 begin_attach_external
Caller authorization: caller is the actor itself, the actor’s deployer, or an account holding dns.attach_external delegated by the actor’s owner.
Generates verification_nonce, sets status = PENDING, computes the challenge value:
current_block + CHALLENGE_EXPIRY_BLOCKS = 43_200 (~12 hours).
5.3 Verification job (uses real CIP-2 primitives — corrects original §9.6)
begin_attach_external enqueues a JobSpec (runner/src/types.rs:101) into JOB_DISPATCHER=0x02:
min_resolvers independent recursive resolvers (operator-configured public list — e.g. Google, Cloudflare, Quad9, OpenDNS) and reports per-resolver match/mismatch. The Result Verifier (0x03) aggregates with MajorityVote semantics.
DNS variability (TTL, anycast routing, edge caching) is absorbed by majority voting; minority disagreement does not trigger slashing unless it falls outside dispute_window_blocks.
The original CIP-16 §9.6 chooses VerificationMode::Deterministic, which (per node/runner/src/types.rs:217 and CLAUDE.md) requires TEE + byte-identical comparison. DNS resolution is not byte-identical — different resolvers see different cached views. MajorityVote is the structurally correct mode and is already implemented (runner/src/types.rs:215).
5.4 Canonical edge target (anycast, with optional SRV)
The original §9.4 prescribesCNAME → edge.cowboy.network and assumes that hostname is anycast. CIP-16-aligned makes the assumption explicit:
- Anycast mode (default):
edge.cowboy.networkis a BGP-anycast A/AAAA record. CNAME target equalsedge.cowboy.network. Browser-compatible without changes. - SRV mode (optional):
edge.cowboy.networkis also published as an SRV record listing active Gateway endpoints (port + priority + weight). Useful for non-browser clients (CLIs, libraries) that respect SRV. Browsers do not query SRV for HTTPS today.
DnsCnameMatch check uses CANONICAL_EDGE_HOSTNAME as the expected target either way.
5.5 TLS / ACME delegation
Same as original §9.5 (DNS-01 via_acme-challenge.<fqdn> CNAME → <token>.acme.cowboy.network). The Cowboy ACME control plane obtains and renews certificates server-side. CIP-16-aligned adds explicit centralization risks to §10 below.
5.6 complete_attach_external (system-mediated — corrects original §9.2)
Replaces the original “system-only callback” hand-wave with an enforceable rule.
Implementation:
- A new
SystemInstruction::ExternalDomainCallback { fqdn, runner_consensus, attestation }(opcode 67 per the canonical master allocation table in CIP-13 v2 §1). - Sender allowlist: only
RESULT_VERIFIER=0x03. The verifier’s normal callback path emits this opcode afterMajorityVoteaggregation succeeds. - The dispatcher routes the opcode to
ROUTE_REGISTRY.complete_attach_external_internal(fqdn, attestation). - The Route Registry verifies
attestation.verification_noncematches the PENDING record, then:status = ACTIVEverified_at = current_blocknext_reverify_at = current_block + EXTERNAL_REVERIFY_INTERVAL- schedules a CIP-5 timer at
next_reverify_atto fire_reverify_external(fqdn).
- Verifier job returns
JobStatus::Failed(runner/src/types.rs:294) → dispatcher emitsSuspendBinding{fqdn, reason}opcode, sender allowlist still0x03. Binding transitions toEXPIRED(challenge window elapsed) or remainsPENDING(still within window — owner may retry).
5.7 Reverification
The CIP-5 timer (one per binding, scheduled in §5.6) fires_reverify_external(fqdn), which:
- Charges
EXTERNAL_REVERIFY_FEE(§5.8) from the binding owner. - If insufficient balance:
status = SUSPENDED, reason =INSUFFICIENT_REVERIFY_FEE. No verifier job dispatched. - Otherwise dispatch the same DNS verification job as §5.3.
- On success: refresh
verified_atandnext_reverify_at; reschedule timer. - On failure:
status = SUSPENDED, reason =TXT_MISMATCH/CNAME_DRIFT/ACME_DELEGATION_MISSING(set by the verifier callback).
reverify_external(fqdn) is also exposed for ad-hoc revalidation (e.g., after fixing a DNS problem on a SUSPENDED binding).
5.8 Reverification fee (new — closes the free-ride gap)
The original CIP-16 leaves external bindings free after attachment; reverify cost falls on the protocol’s verifier capacity indefinitely. CIP-16-aligned introduces a small recurring fee:system:cip16_registry_settlement (same splits as registration fees). Insufficient balance → SUSPENDED (§5.7).
A binding owner can prepay reverify cost by holding sufficient balance; the protocol does not require an explicit “deposit”. Owners who let their balance run out get suspended, not slashed — recovery is reverify_external(fqdn) after topping up.
5.9 Detachment
detach_external(fqdn) — caller is owner. Unschedules the reverify timer, transitions to DETACHED, allows the FQDN to be reattached later via a fresh begin_attach_external.
5.10 Reverify timer fee_payer (CIP-5 revision alignment)
CIP-5 (revised 2026-04-20) §6.3 introduces a per-fire fee_payer model: every CIP-5 timer fire is itself metered and pre-charged. The reverify timer scheduled in §5.6 / §5.7 MUST be scheduled with fee_payer = binding.owner.
Two distinct fee surfaces now apply to a single reverification:
Because the timer-fire charge happens BEFORE the handler runs, an owner who can’t cover
max_cost will see the timer silently self-destruct without ever entering _reverify_external. The binding stays in its prior status (typically ACTIVE) but no reverification occurs — and the next reverify is never scheduled either, since scheduling happens inside _reverify_external step 4. Without intervention this leaves the binding in a “frozen-ACTIVE” state past next_reverify_at.
Note (on-chain status, 2026-07). For first-party TLD labels this is not exploitable: the deployed resolver (Mitigation:resolve_serveable_binding,execution/src/runner/domain.rs) enforces term lazily — it returnsNonewhenTldLabelRecord.expires_at <= block_heightregardless of the binding’s storedstatus, so a stuck-ACTIVEbinding is never served past its term (testtld_label_expires_and_is_reclaimable; ratchetesc-20260608-cip16-expiry-inert). The external-domain reverify lifecycle described in this section (_reverify_external,ExternalDomainCallback,EXTERNAL_REVERIFY_FEE) is spec-only / not yet on-chain; when built, external bindings (which carry noTldLabelRecord) will need the status rewrite this mitigation describes.
- The Route Registry MUST subscribe to
TimerCancelledInsufficientFundsevents whosetimer_idmatches a scheduled reverify timer (looked up via the reverify-timer index it maintains). - On receipt, the Route Registry transitions the corresponding binding to
SUSPENDEDwith reasonINSUFFICIENT_TIMER_FUEL(a new reason code distinct fromINSUFFICIENT_REVERIFY_FEE). - §7.1’s existing
current_block > next_reverify_at + EXTERNAL_REVERIFY_GRACE_BLOCKSoverdue check provides a second-line defense if the event-subscription path is missed.
SUSPENDED reason to §6:
reverify_external(fqdn).
6. Status transitions
SUSPENDED reasons enumerated: TXT_MISMATCH, CNAME_DRIFT, ACME_DELEGATION_MISSING, INSUFFICIENT_REVERIFY_FEE.
7. Gateway behavior
7.1 Resolution
Gateway queries the Route Registry with the fully normalized FQDN. For external bindings, additionally:- Status must be
ACTIVE. current_block ≤ next_reverify_at + EXTERNAL_REVERIFY_GRACE_BLOCKS. Defense against a Gateway with stale state: even if the on-chainstatusis stillACTIVE, treat the binding as effectivelySUSPENDEDif reverification is overdue.- TLS certificate present and valid (Gateway control plane responsibility, not on-chain).
7.2 Serving policy (corrected status codes)
The original §10.2 suggested
421 Misdirected Request for SUSPENDED. HTTP/2’s 421 semantics (“this server is not authoritative for the requested URI”) instructs the client to retry against a different connection / Gateway, which is the wrong behavior when the binding itself is suspended — no other Gateway will succeed either. 503 correctly signals “temporary service unavailability” and avoids unnecessary retries.
7.3 Verified host injection (corrects host-header trust gap)
TheIngressDispatch instruction (CIP-14-aligned §6.1) injects two extra fields into the HttpRequestEnvelope:
verified_fqdn (and namespace_kind if multi-binding) rather than the raw host header, which is set by the client and is attacker-controllable in the absence of system mediation. The original host field is retained for backward compatibility but is for informational use only.
A multi-binding actor (e.g., one serving both api.alice.cow and api.example.com) uses verified_fqdn to route per-tenant — this is the only field the protocol attests.
7.4 First-party TLD DNS
Same as original §10.3. Cowboy-controlled DNS authority for.cow / .cowboy serves records directly from the Route Registry namespace. No TXT proof or external ACME delegation needed for first-party labels.
8. Constants
9. Security delta vs. original
10. Known centralization risks (explicit — extends original §11.5)
The following are inherent to interoperating with the existing DNS / TLS ecosystem at v1. Mitigation is operational (multi-sig over zone updates, certificate transparency monitoring), not protocol-level:- ACME control plane (
_acme-challenge → acme.cowboy.network): the Cowboy-operated ACME zone holds delegated authority for issuance against attached domains. Compromise of this zone enables certificate misissuance for any attached domain. A future CIP may introduce a multi-party ACME design or “bring your own certificate” mode. - Anycast edge (
edge.cowboy.network): a single hostname that all attached domains CNAME to. The DNS authoritative for this name is a centralized component; anycast routes traffic but does not distribute authority over the name itself. - First-party TLD authority (
.cow,.cowboy): Cowboy’s registry-level operation is a separately governed trust point. ICANN-rooted DNS prevents this from being trustless at v1. - DNS verifier executor (
DNS_VERIFIER_EXECUTOR_HASH): the hash is governance-pinned, meaning governance can change the verification logic by updating the hash. A compromised governance multisig could approve a verifier that always returnsmatch.
11. Backwards compatibility
Additive over CIP-14-aligned. TheRouteRegistration → DomainBinding migration (§3.1) is a one-time schema upgrade triggered at the protocol upgrade boundary; it does not change behavior for existing records — they continue to resolve through the cowboy.network namespace with status = ACTIVE defaults and no reverification cycle.
Gateways without CIP-16-aligned implementation simply cannot serve first-party TLD or external attached domains — *.cowboy.network continues to work.
12. Future work
Part III — Cross-Cutting Conventions (verbatim from former alignment-conventions.md)
Alignment Conventions for CIP-14 / CIP-15 / CIP-16
Status: Draft alignment companion (non-modifying) Created: 2026-04-21 Scope: Cross-cutting conventions used bycip-14-dns-addressable-actors-v2.md (Part II), cip-15-public-asset-hosting-v2.md (Part II), Part II of this document. Anything that would otherwise be repeated across all three drafts lives here.
This document also enumerates upstream amendments these aligned drafts assume in CIP-2, CIP-3, CIP-5, CIP-9, and the normative entitlement registry — without modifying those source documents. Each AMEND item is a precondition: implementing CIP-14/15/16 requires the corresponding amendment to land first.
1. System actor address allocation
The current low-byte sequence (node/types/src/constants.rs, node/runner/src/system_actors.rs:11-33) ends at 0x0C (SESSION_ACTOR). The aligned drafts continue the same dense sequence (0x0D, 0x0E, 0x0F, …) rather than the two-byte 0x0011/0x0012 numbering used by the original CIP-14 draft.
Rationale: keeping the sequence dense matches
system_actors.rs convention. Original CIP-14 v1 numbers (0x0011 / 0x0012) are renumbered to 0x0E / 0x0F. WP §9 is the canonical cross-CIP allocation table.
2. Entitlement registry amendments (entitlement spec §9)
Adopting the aligned drafts requires three new entries innode/types/src/registry.rs::REGISTRY. The registry is lexicographically sorted (enforced by registry_is_sorted_lexicographically); insert at the indicated positions.
2.1 ingress.http (CIP-14)
http.fetch and oracle.llm.
quota: false is intentional and differs from the original CIP-14 §6.1 table. The manifest has no on-chain quota accumulation mechanism: every max_* value is a per-request limit, not a cumulative quota. The flag matches reality.
2.2 ingress.static (CIP-15)
ingress.http.
This is a separate entitlement, not an extension of ingress.http. The original CIP-15 §7.1 nests array<StaticVolumeBinding> (object array) inside ingress.http.params.static_volumes — but ParamValue (node/types/src/manifest.rs:29-34) only supports Uint / Str / StrArray / AddressArray. There is no Object variant and adding one would touch manifest serialization, signature digests, and codec round-trip tests for every existing actor.
static_volume_names: StrArray lists volume names by ordinal; per-volume cache budgets collapse to a single max_cache_bytes_total (Gateway operators may apply local LRU splits — not protocol-enforced).
2.3 dns.attach_external (CIP-16)
bridge.subscribe_event and econ.hold_balance.
Required so an actor can be the target of begin_attach_external. First-party TLD and cowboy.network registrations remain governed only by ingress.http.
2.4 Test update
The registry-size test is nowregistry_has_exactly_19_entries (node/types/src/registry.rs). The current 19 entries include ingress.http (already deployed and enforced by the Route Registry handlers via require_target_ingress_http), secrets.verify, and econ.intent; the earlier “14 → 17” migration note is stale.
3. ParamValue limits (binding for spec authors)
ParamValue only supports four shapes (node/types/src/manifest.rs:29-34). The aligned drafts conform to this without proposing a ParamValue::Object variant, because that change would force a coordinated schema migration of every deployed manifest.
Allowed:
- a scalar
Uint(≤u64) - a single
Str(≤ 256 bytes) - a
StrArray(≤ 64 entries × 256 bytes) - an
AddressArray(≤ 64 addresses)
- nested objects → flatten to multiple entitlements, or two parallel
StrArrays pairing by index - booleans → use
Uintwith0/1 - arrays of structs → use parallel arrays
- maps → store JSON in a
Str(deploy-time validation cannot recurse into the JSON)
STORAGE_MANAGER record or a separate system-actor table that the actor owner updates by transaction).
4. System-mediated handler invocation pattern
Several flows in CIP-14-aligned and CIP-16-aligned require an actor to trust that a specific selector was invoked only by a specific system actor (e.g.GATEWAY_REGISTRY=0x0F, RESULT_VERIFIER=0x03). The aligned drafts implement this in the system-instruction dispatcher (node/execution/src/system_instruction.rs) rather than relying on SDK-side ctx.sender checks.
The pattern (matches the existing BASEFEE_SYSTEM_ACTOR=0x06 idiom for UpdateBasefee):
- Define a new
SystemInstructionopcode (e.g.IngressDispatch,ExternalDomainCallback) carrying(target_actor, selector, payload). - The dispatch handler enforces a sender allowlist: only the named system actor address may emit the opcode.
- The dispatcher synthesises an internal
ActorMessagewhosectx.senderis set to the system actor address. Ordinarysend_message/call_actorfrom arbitrary accounts cannot reproduce thisctx.sendervalue because the message router populatesctx.senderfrom the calling tx’s signer (it cannot be forged by the caller’s own code). - Receiving actors MUST verify
ctx.senderagainst the canonical sender for that selector (e.g.ctx.sender == GATEWAY_REGISTRY=0x0Ffor"http.request";ctx.sender == RESULT_VERIFIER=0x03for"_dns.callback"). The SDK (CIP-6) decorator-based handlers MUST include this check by default; raw handlers MUST include it manually.
Note (revision). An earlier draft described a 4th step in which the PVM message router would additionally reserve the corresponding selectors. That proposal was withdrawn (seeThis makes ingress / verifier authenticity a protocol property:cip-14-dns-addressable-actors-v2.md(Part II) §6.2 Note) because it broke legitimate router-actor forwarding patterns. The handler-sidectx.sendercheck above is sufficient.
ctx.sender is set by the message router from on-chain signer state. SDK-default sender checks at the receiving handler are mandatory.
5. Read-only handler execution (replaces “queryActor”)
CIP-14-aligned introduces a new RPC and a corresponding PVM mode. The current node RPC layer (node/rpc/src/rpc.rs:140-210) has no read-only handler invocation today — only REST committed-state reads (/actor/{addr}/storage, etc.). The original CIP-14 cites a “Milestone 2 §5.2 queryActor primitive” that is not present in the codebase.
5.1 RPC
5.2 PVM mode
PvmExecutor::execute_handler gains a read_only: bool argument. When true, the host:
- Returns from
state_get/state_scan_prefixas today. - Traps on every mutating syscall — see §5.3 for the exhaustive table.
- Returns
Address::ZEROforcallerandNoneforctx.sender(no transaction context).
HostContext flag plus per-syscall guard clauses.
5.3 Permitted vs. trapped syscalls (definitive table)
Names matchnode/execution/src/pvm_host.rs exactly. This table supersedes the original CIP-14 §8.3.1, which used several syscall names that do not exist in the host (e.g. set_storage, set_timeout, transfer, create_volume, entitlement_params).
Ambient context syscalls (
block_height, block_timestamp, self_address) are permitted; they read fields from HostContext rather than calling the host trait.
Trap code: ERR_READONLY_VIOLATION (new). Gateway maps to HTTP 500 with X-Cowboy-Error: READ_ONLY_VIOLATION.
The randomness trap fixes a bug in the original CIP-14 §11.3 determinism argument — randomness is exposed at pvm_host.rs:1372 and would have allowed read-path divergence between Gateways without this trap.
6. Settlement / fee distribution reuse
CIP-3 routes burn / treasury / runner-tip splits throughSettlementConfig stored at GOVERNANCE_SYSTEM_ACTOR=0x09 under key system:settlement_config, updatable via UpdateSettlementConfig (opcode 40, sender must be 0x09 per system_instruction.rs).
The aligned drafts add two parallel configs under the same governance actor (no new system actor required):
system:cip16_registry_settlement— splits for name registration / renewal fees (CIP-14-aligned §4.5, CIP-16-aligned §4)system:gateway_pool_config— splits for the Gateway serving fee pool (CIP-14-aligned §7.4)
UpdateSettlementConfig opcode with a new target_pool discriminant (one new enum variant; not a new opcode). This avoids forking burn/treasury routing across multiple ad-hoc paths.
7. CIP-9 amendments (precondition for CIP-15-aligned)
Errata note. An earlier revision of this section (v1) listed AMEND 9-A through 9-E claiming thatCIP-9 already provides the bulk of what CIP-15-aligned needs:StorageCommitment,commit_manifest, thevolume_id = keccak256(...)formula, andVisibility::Publicwere missing. They are NOT missing — they are all in CIP-9 today. The corrected, smaller delta list is below; details are incip-9-runner-storage-v2.md(Part II).
StorageCommitmentwithvolume_id,owner,visibility,manifest_root,status(CIP-9 §11.1).commit_manifest(cap_token, manifest_root)system instruction (CIP-9 §12.2).volume_id = keccak256(account_address || volume_name)(CIP-9 §11.1).Visibility::Publicmodel for unauthenticated reads via shard metadata (CIP-9 §7.6.3) — note: original CIP-15 usedPUBLIC_READ; the canonical CIP-9 / CBFS name isVisibility::Public.- Volume status state machine
ACTIVE → GRACE_PERIOD → DELETED → GARBAGE_COLLECTING(CIP-9 §13).
cip-9-runner-storage-v2.md (Part II). Summary:
- AMEND 9-G —
GET_MANIFESTRelay Node RPC (cip-9-runner-storage-v2.md(Part II) §2). Direct manifest fetch in one round trip without per-shard reconstruction; required for low-latency Gateway operation againstVisibility::Publicvolumes. - AMEND 9-H —
ManifestCommittedchain event (cip-9-runner-storage-v2.md(Part II) §4). Powers Gateway eager cache invalidation; polling remains as a floor. - Pin canonical manifest serialization to
cbfs/manifest/src/merkle.rs(cip-9-runner-storage-v2.md(Part II) §3). Reuses existing CBFS bincode + RFC-6962-style Merkle (avoids the Bitcoin-style duplicate-last-leaf shape). - Gateway serving authority mapped from existing CIP-9 statuses (
cip-9-runner-storage-v2.md(Part II) §5). Uses existingACTIVE/GRACE_PERIOD/DELETED/GARBAGE_COLLECTINGrather than introducing a newDELINQUENTstatus.
GET_SHARD against __manifest__) and time-based polling — at the cost of higher per-request latency and slower invalidation.
8. CIP-2 amendments (precondition for CIP-16-aligned)
The currentrunner/src/types.rs::VerifierCheck enum has no DNS primitive (runner/src/types.rs:177-201). CIP-16-aligned uses CIP-2 multi-runner verification with VerificationMode::MajorityVote (already implemented per runner/src/types.rs:215) and two new check variants:
- AMEND 2-A — Add
VerifierCheck::DnsTxtRecordMatch { fqdn: String, expected_value: String, min_resolvers: u32 }. Verifier runners resolvefqdnviamin_resolversindependent recursive resolvers (operator-configured public list) and report match / mismatch. - AMEND 2-B — Add
VerifierCheck::DnsCnameMatch { fqdn: String, expected_target: String, min_resolvers: u32 }. Used to check the canonical-edge CNAME. - AMEND 2-C —
JobType::Custom { executor_hash, params }already exists (runner/src/types.rs:146-149). CIP-16-aligned uses it for the verification job;executor_hashreferences a built-in DNS-verification executor whose hash is governance-pinned (DNS_VERIFIER_EXECUTOR_HASH).
VerificationMode::Deterministic, which (per node/runner/src/types.rs:217 semantics and CLAUDE.md) requires TEE + byte-identical comparison. DNS resolution is not byte-identical across resolvers / cache states; MajorityVote is the structurally correct mode.
9. CIP-5 amendments
None required. The aligned drafts use existingschedule_timer, schedule_timer_ex, extend_timer, cancel_timer syscalls without changes. The hard ceiling MAX_TIMERS_PER_ACTOR=1024 (node/types/src/constants.rs) is treated as a constraint that motivates §10 below.
10. Receipt model (replaces SDK-conventional _http/results/{request_id})
The original CIP-14 §8.4 stores command-path results in actor KV at _http/results/{request_id} and registers a per-request cleanup timer. With MAX_TIMERS_PER_ACTOR = 1024, a popular API actor exhausts its timer budget within ~1k pending requests.
CIP-14-aligned defines a RECEIPT_REGISTRY=0x10 system actor that owns receipt storage and lifetime. See cip-14-dns-addressable-actors-v2.md (Part II) §8 for the full schema. Two key properties:
- Receipts are written by the
IngressDispatchsystem instruction post-handler-return, not by actor code. Actors do not consume their own KV or timer budget for receipt management. - A single registry-wide pruning loop expires receipts via TTL, replacing per-request timers. One timer slot total, not one per pending request.

