Naming and Addressing #
How a site is addressed, how each thing on it gets an address, and how it gets a name.
The plan is fixed per site. How a thing gets an address and a name from that plan is deliberately boring for the substrate and deliberately different for tenants. This page sets out the plan, then the chain from declaration to address and name, where authority changes hands, and what each side is allowed to write.
The Site Address Plan #
Site Blocks #
Each site is assigned a /16 block from the 10.0.0.0/8 RFC1918 space:
| Site | Address Block |
|---|---|
| home (reserved, not built) | 10.10.0.0/16 |
| mobile | 10.20.0.0/16 |
The addressing pattern is: 10.{site_id}.{vlan_id}.0/24
- The second octet identifies the site
- The third octet matches the VLAN ID for that segment
- Each segment subnet is a /24 within the site’s /16
This creates a predictable, self-documenting address scheme where any IP immediately reveals which site and segment it belongs to.
Splitting the Site Block #
The pattern above describes substrate segments, where the third octet is a VLAN ID. Tenant
overlays are not VLANs, so they cannot be described by it. Each site’s /16 is therefore split,
per
ADR-0002:
| Block (mobile) | Purpose |
|---|---|
10.20.0.0/17 | Substrate segments β third octet = VLAN ID, as above |
10.20.128.0/18 | Tenant overlay subnets β 10.20.{128+n}.0/24 for tenant index n |
10.20.255.0/24 | Tenant fabric loopbacks / VTEP identity |
The reserved home block would mirror this in 10.10.0.0/16. Keeping tenants inside the site block means there is still
exactly one aggregate per site: anything that ever has to route to a site routes one prefix,
and that prefix keeps covering its tenants unchanged.
So the third octet also tells you which side of the line an address is on: below 128 is a
substrate segment, 128 and above is a tenant overlay.
Reserved VLAN Ranges #
| VLAN Range | Purpose |
|---|---|
| 10-40 | Core segment types (trusted, storage, platform, IoT, guest) |
| 50-59 | Tenant fabric transport (transit, underlay) β not a network per tenant |
| 60-69 | Reserved for future segment types |
| 70-79 | Experimental/lab segments |
| 99 | Management |
Gateway Convention #
Each subnet uses .1 as the gateway address:
10.20.30.1β mobile IoT segment gateway10.20.99.1β mobile management segment gateway
Host Addressing Ranges #
| Range | Purpose |
|---|---|
| .1 | Gateway (core router VLAN interface) |
| .2-.49 | Static infrastructure hosts |
| .50-.59 | Tenant-reserved addresses |
| .60-.69 | Reserved for future use |
| .70-.79 | Experimental/lab use, including transient addresses a build holds only while it runs |
| .100-.200 | DHCP dynamic pools (where applicable) |
Infrastructure hosts (routers, hypervisors, provisioners, switches, APs) receive static assignments in the low range. DHCP pools are used for segments with dynamic devices (trusted, IoT, guest).
A device network that carries tenants’ devices is divided differently, in three parts: substrate-owned hosts at the bottom, a range from which tenants reserve addresses for their devices in the middle, and the dynamic pool at the top (
ADR-0035). On the mobile site’s IoT network those are below .25, .25 to .200, and .201 to .254.
WAN Operation #
mobile operates the same way wherever it is: behind its edge router, with outbound NAT to whatever upstream is available (home, a hotel, a tethered phone). The edge router takes its own address from the upstream and NATs all of the site’s traffic, so the site’s addressing never has to change to fit a location.
There is no connectivity between sites. Wherever mobile is set up, it is just another client of that location’s upstream, and no site routes to another. Linking the sites is a decision for when there is a reason to, and would be made as a declared site link with its own policy, not by renumbering or by turning NAT off.
How a Substrate Host Gets Its Address and Name #
Nothing on the substrate picks its own address, and nothing invents its own name. A single declared identity produces both:
graph LR
ID["Declared identity
(inventory)"] --> MAC["Hardware address"]
MAC --> RES["Address reservation"]
RES --> IP["Address"]
IP --> REC["Name record"]
REC --> NAME["service.site.deevnet.net"]
Every link is derived, not chosen:
| Link | Where it comes from |
|---|---|
| Identity β hardware address | Assigned from the estate’s own address namespace, derived from the host’s allocated identifier. Addresses on hardware we did not create are recorded as found, never rewritten. |
| Hardware address β address | A reservation on the address service, generated from inventory |
| Address β name | A record on the resolver, generated from the same inventory entry |
The consequence is that a host’s address is a fact about its declaration, not about the order it booted in. Rebuild it, and it comes back on the same address with the same name, because nothing along that chain was decided at runtime.
Reservations, Not Static Configuration #
Substrate hosts are addressed by reservation: they ask for an address, and the address service always gives them the same one because it recognizes their hardware address. They are not configured with an address locally.
This looks like a detour β why not just configure the address on the host? β but it puts the addressing plan in one place that can be reviewed, and it means a host with a fresh operating system and no configuration at all still arrives at the right address. The declaration is authoritative; the host is disposable.
It also means the hardware address is load-bearing. A host whose hardware address does not match its declaration does not get its reserved address. It gets whatever the dynamic pool hands out, which is a working address that no name points at β a failure that looks like success until something tries to reach the host by name.
Segments Differ #
Not every segment is reservation-only. Segments carrying transient devices have dynamic pools; management and platform segments do not, because everything on them is declared. Where a pool and reservations coexist on one segment, the pool is positioned above the reserved range so the two cannot collide.
Tenants Address Themselves #
Tenant workloads do not use the substrate’s address service, and the reason is structural rather than preferential: a tenant’s network is an overlay owned by the tenant compute domain ( ADR-0001), and that overlay has no leasing service of its own.
So a tenant workload is given its address at creation time, from the tenant’s own code, derived from the tenant’s index ( ADR-0002). There is no reservation because there is nothing to reserve against.
| Substrate host | Tenant workload | |
|---|---|---|
| Address decided by | Reservation, from declared identity | The tenant’s own code, at creation |
| Address service involved | Yes | No |
| Survives a rebuild because | The reservation is derived from inventory | The address is derived from the tenant index |
Both are deterministic. They arrive there by different routes because they live on different networks with different capabilities, and pretending otherwise would mean adding a leasing service to the overlay purely for symmetry.
Two Naming Authorities #
Names split along the same seam as the tenant contract: the substrate names the things it runs, and a tenant names the things it runs.
graph TB
CLIENT["Any substrate client"] --> RESOLVER["Substrate resolver
site.deevnet.net"]
RESOLVER -->|"substrate names"| SUB["Answered directly
records generated from inventory"]
RESOLVER -->|"tenant zone"| AUTH["Tenant authoritative service"]
AUTH --> TEN["Answered from the tenant's zone
records written by the tenant"]
The substrate resolver answers for substrate names. Its records are generated from inventory by the same automation that generates the reservations, so the address and the name cannot disagree β they come from one declaration.
A tenant’s names live in a separate authoritative service. The substrate runs that service and creates each tenant’s zone, but it never writes a record into one. Records are tenant content ( ADR-0004).
This is the point of the arrangement. If tenant names lived in the substrate resolver, then either tenants would need to commit to a substrate repository to publish a name, or substrate reconciliation would be free to delete records it did not create. Separating the zone removes the conflict rather than managing it.
At the Mobile Factory a tenant’s zone is its site zone, <tenant>.mobile.deevnet.net, and nothing
else. A tenant service’s identity is its site-less name, <service>.<tenant>.deevnet.net, but
that name resolves only at a site where the tenant runs in production, and the factory, which builds
tenants, is not one
(
ADR-0040 Β§1). Both
names are in the service’s certificate, so where a name resolves never changes what a client trusts.
How a Query Reaches a Tenant Name #
This is the part worth being precise about, because the common shorthand is misleading.
A substrate client asks the substrate resolver for everything. When the name falls inside a tenant’s zone, the resolver forwards the query to the tenant authoritative service and returns its answer.
sequenceDiagram
participant C as Client
participant R as Substrate resolver
participant A as Tenant authoritative service
C->>R: api.tenant.site.deevnet.net?
R->>A: forwarded β this zone is yours
A->>R: answer from the tenant's zone
R->>C: answer
Forwarding Is Not Referral #
The resolver is configured with a rule of the form “for this zone, ask that server”. It is not following a delegation: no parent zone contains a referral to the tenant’s servers, and the resolver never asks the tenant zone which servers are authoritative for it.
That distinction has real consequences, and they are easy to miss:
- The tenant zone’s own apex records are never consulted on this path. A zone missing the records that name its own servers still resolves perfectly through the resolver, because nothing on this path ever looks at them. The defect is invisible from the client’s point of view β which is exactly how it went unnoticed until ADR-0005.
- Resolution depends on configuration, not on the DNS hierarchy. Anyone querying the tenant authoritative service directly gets the same answers; anyone querying the public hierarchy for these names gets nothing. These are internal names and that is intended.
- Anything that does validate the hierarchy will not be satisfied by this. A second authoritative server, a resolver that checks a child’s servers against its parent, or a genuine referral would all need the zone apex to be correct. Which is why it is maintained even though this path never reads it.
Calling it a delegation is fair as a description of ownership β the tenant owns the zone. It is inaccurate as a description of mechanism, and the difference is where the surprises live.
Two Write Paths #
The same split appears in how records get written.
| Substrate names | Tenant names | |
|---|---|---|
| Source of truth | Inventory | The tenant’s own IaC |
| Written by | Substrate automation, reconciling | The tenant, at deploy time |
| Transport | The resolver’s own configuration interface | Dynamic update, over the network |
| Authorization | Access to the substrate repository | A credential scoped to that tenant’s zones |
| Blast radius of a mistake | The substrate zone | That tenant’s zone only |
The tenant’s credential is the part that makes the boundary real. It is issued per tenant and accepted only for that tenant’s zones, so a tenant writing outside its namespace is refused by the server rather than trusted not to try.
One qualification, recorded because the table above reads stronger than the truth: the credential is scoped to a zone, not to individual records within it. Everything inside a tenant’s own zone β including the apex records the substrate maintains β is writable by the tenant holding that credential. The namespace boundary is enforced; the boundary within a zone is a convention. See ADR-0005.
Reverse Names #
Reverse lookup follows the same shape. Each tenant gets a reverse zone alongside its forward zone, derived from the same tenant index, forwarded the same way, and populated β or not β by the tenant. Substrate reverse records are generated from inventory with everything else.
What This Arrangement Guarantees #
- A rebuilt host returns to its own address and name, because both derive from a declaration rather than from anything that happened at runtime.
- A rebuilt tenant restores its own names, with no substrate change, because the records were never substrate content.
- A rebuilt resolver loses no tenant names. Its share of the arrangement is one forwarding rule per tenant zone, regenerated from inventory; the records themselves were never in it.
- Neither side can quietly overwrite the other. They do not share a zone, so there is no reconciliation that could.
The cost is one more service to run and one more thing to be down. Tenant names depend on the authoritative service being up; substrate names do not, and continue to resolve if it is not.