Identity & Naming
Principal types, CA management, and SPIFFE naming scheme for C0
Identity Model & CA
Types of Principal
A principal is any entity with a rete identity — a SPIFFE ID issued by the rete CA. Users, services, nodes, vertices, and the signing keys are all principals (the rete CA itself is not — it's the issuing authority, see Rete CA). "Principal" denotes having an identity, independent of what the entity does on a given connection: a service that only ever serves is no less a principal than a user that only ever initiates. The directional roles a workload plays in a flow — initiator vs target — are orthogonal to the principal kinds below and are defined in Validate & Compile.
C0 issues two flavours of SPIFFE principal, distinguished by how the cert is used at runtime. Both flavours are signed by the same rete CA and live in the same trust domain; what differs is the X.509 extension policy and where the private key resides.
TLS principals — terminate mTLS handshakes on the wire. Cert has keyUsage: digitalSignature, keyEncipherment plus extKeyUsage: serverAuth, clientAuth. The private key is held by a flor vertex acting on the principal's behalf.
| Kind | Exists in C0 | Owner | Used for |
|---|---|---|---|
| User | Yes | The human (their workload) | End-to-end mTLS to services |
| Service | Yes | The service workload | End-to-end mTLS + as caller |
| Node | Yes | The node (the host as rete member) | Mgmt-plane mTLS — agent ↔ rete infrastructure (config-server, metrics) |
| Vertex | Reserved | Rete mesh-vertex | Hop-by-hop link-layer mTLS (introduced in C1) |
Signing principals — sign envelopes at compile or decision time. Cert has keyUsage: digitalSignature only, no extKeyUsage — TLS verifiers reject these certs at handshake, so a leaked signing cert cannot be re-used to impersonate a TLS endpoint. The private key stays on the producer's own machine and is never held by a vertex. Only the public cert is shipped to nodes (delivered alongside ca.crt at enrollment) so agents can verify envelope signatures locally.
| Kind | Exists in C0 | Owner | Used for |
|---|---|---|---|
| ManagementPlane | Yes | The operator (separate keypair from their user identity) | Signing mgmt artifacts (envelope key_id) |
| ControlPlane | Reserved | The CP signing authority (operator-held in C1, cloud- or BYO-held in B1+) | Signing ctrl artifacts (introduced in C1) |
All four C0-active and two reserved kinds are recognised from day one — see ADR-0005. Pre-declaring the reservations means C1 can introduce Vertex and ControlPlane runtime usage without touching the CLI --kind parser, the Kind enum, or the cert-extension policy table.
Rete CA
The rete's root CA, generated by retectl ca init, issues every cert in both tables above. The CA itself is not a principal — it's the issuing authority. Its private key never participates in mTLS or envelope signing directly; only the certs it signs do. The CA cert (ca.crt) lives in the repo and is shipped to every node; the CA private key stays on operator hardware (password-protected file, B1+ moves it to an HSM or step-ca-style intermediate-CA service).
How TLS principal material flows
Each TLS principal's cert+key are held by a flor vertex acting on the principal's behalf. In C0 there's only one vertex per node, so it holds the node identity (for the agent), every user identity hosted on that node, and every service identity hosted on that node — all on their respective owners' behalves. The vertex isn't any of those principals, just like C0 flor isn't alice or api; it carries their material because that's where mTLS terminates.
How signing principal material flows
Signing keys live on the producer (the operator for management-plane; the CP service for control-plane in B1+). No vertex ever loads them. Agents receive only the public certs at enrollment and use them to verify the signature on each fetched artifact. Rotation of a signing principal means issuing a fresh cert from the CA and shipping the new pubkey to agents in the next enrollment bundle (C0); finer-grained rotation mechanisms are deferred to B1+.
Three distinct things called 'node'
These terms overlap in everyday speech but are distinct in the Florete model:
- Node (computing host) — a physical machine, VM, or device. A hardware/OS concept. May run workloads from multiple Florete retes simultaneously.
node/<name>SPIFFE principal — the node's rete-scoped identity, one per (host, rete) pair. Exists within a specific rete's trust domain; the name is chosen by that rete's operator and may differ across retes the same host has joined.flor-agent— the workload process that acts on the node-principal's behalf within one rete. Fetches state, supervises vertices, dials config-server using the node identity. One agent process per joined rete.
"Per-node" and "for all nodes" in rete-scoped prose always mean per node/<name> principal in this rete, not per physical host. The distinction matters when one host joins multiple retes.
Key points:
- The node is a first-class principal;
flor-agentis the workload that acts on its behalf. The node's identity (spiffe://<rete>/node/<node>) is the rete's view of this computing host — used solely for agent ↔ rete-infrastructure mTLS (fetching compiled state from the config-server, pushing metrics). The agent does not hold the cert+key; the local flor vertex does, and the agent delegates mTLS to it via a SOCKS5 channel, the same way alice delegates to flor. Node identity never appears on the workload hot path: user-to-service and service-to-service traffic is authenticated end-to-end between those principals. - Services are first-class principals. A service's identity is both the server side of its inbound mTLS and the client side of its outbound connections. The
rolefield in services.yaml governs what the service can call. - Node identity and mesh-vertex identity are distinct principal kinds with distinct holders. C1 introduces
vertex/<node>/<name>for hop-by-hop mesh-transit mTLS: the principal is mesh-flor (qua participant in the link layer), and link-flor below it holds the cert+key. Thenode/<node>principal is the node itself, with flor-agent as its workload. The two kinds belong to different layers and different holders — there's no question of merging. A computing host can join multiple Florete retes; each rete gives it a separatenode/<name>identity in its own trust domain, with a dedicated flor-agent process acting on its behalf.
CA Management
- Rete CA: one root CA per Florete rete, generated by
retectl ca init. Publicca.crtlives in the git repo atcerts/ca.crt; private key stays on the operator's workstation. - Authentication: mTLS handshake verifies the CA signature. No pinned-cert maintenance on every node.
- Authorization: ACLs (roles → groups → services) operate on the authenticated principal name.
- Private keys: stay on the machine that generated them (
~/.flor/retes/<scope>/<name>.key). Never in the repo. - Signed certs: delivered to the principal that holds the matching private key; stored locally at
~/.flor/retes/<scope>/<name>.crt. Not committed to the repo. CA cert (ca.crt) is the only shared cert in git. - Audit:
enrollment.login the repo records every sign event (operator identity, principal name, role, timestamp, cert fingerprint). Append-only, optionally GPG-signed by the operator. - Revocation in C0: remove the principal's entry from
users.yaml/services.yamland append a revoke-event toenrollment.log. Operator runsretectl compile→retectl publish. The nextflor agent syncdrops the principal from every affected node's ingress table, so even if the revoked private key is still on a machine somewhere, it can no longer pass any peer's access check. CRL/OCSP is deferred until B1.
CA Key Protection
- C0: password-protected file on operator's laptop.
- B1: dedicated CA service (step-ca or similar), HSM-backed root, short-lived intermediate, automatic rotation — moving toward full SPIFFE SVID issuance.
Multi-service On a Single Node
Without container namespaces, we distinguish per-service egress by port binding:
- Inbound (services accepting connections): flor decrypts incoming Florete traffic and forwards to the service's local
addr. No ambiguity — the target service is carried by the QUIC ALPN / SNI and verified against the service identity. - Outbound (services initiating connections): flor exposes a per-service SOCKS5 proxy on a dedicated local port (the
socks5_proxyfield in services.yaml). The service is configured withALL_PROXY=socks5://localhost:<that_port>. Each SOCKS5 port is bound to exactly one service identity, so flor unambiguously knows which principal is calling.
Most HTTP clients, gRPC clients, and database drivers support SOCKS5 via environment variable. Apps that can't are a later concern (options: transparent iptables redirect, SO_PEERCRED with per-service users, libc shim — none are C0 blockers).
Naming Scheme
Florete identities are SPIFFE IDs. The rete name is the SPIFFE trust domain; principal kind is the first path segment; the principal's own name follows. This produces real SPIFFE X.509-SVIDs — the certs flor issues interoperate (in principle) with SPIRE, Istio, Linkerd, Vault, and any other SPIFFE consumer.
Two forms are used interchangeably: the canonical SPIFFE URI (in certs as SAN, in compiled artifacts, in ACLs) and the convenience hostname (for user-facing tooling and shell command-lines) — the latter exists only for services, since users are not things you curl or ssh to. The on-wire routing hint (QUIC SNI) is a third, separate thing: derived from the identity and resolved by lookup rather than parsed, decoupled from the convenience hostname and free to evolve — see ADR-0007: Decouple Naming, Identity, and Routing.
Canonical SPIFFE URI
spiffe://<rete>/user/<user> # user principal
spiffe://<rete>/service/<service> # rete-scoped service
spiffe://<rete>/service/<node>/<service> # node-scoped service
spiffe://<rete>/node/<node> # node principal (used by flor-agent for mgmt-plane traffic)
spiffe://<rete>/management-plane/<name> # mgmt-envelope signing identity (operator-held, sign-only cert)
spiffe://<rete>/control-plane/<name> # ctrl-envelope signing identity (added in C1)Examples:
spiffe://rete-lovers/user/alice— user principal.spiffe://rete-lovers/service/api— api service (rete-scoped).spiffe://rete-lovers/service/alpha/ssh— SSH on node alpha.spiffe://rete-lovers/node/alpha— alpha's node principal (used to reach the config-server and metrics services over Florete).spiffe://rete-lovers/management-plane/primary— the rete's mgmt-envelope signing identity (operator-held, issued explicitly viaretectl ca sign --kind management-plane --name primary). The name is operator-chosen;primaryis the convention, mirroringcontrol-plane/primaryin C1.spiffe://rete-lovers/control-plane/primary— the rete's primary CP signing identity (operator-held in C0/C1; cloud-held in B1+).
C1 extends this with two further kinds:
spiffe://<rete>/vertex/<node>/<name>— for hop-by-hop mesh-transit mTLS. Node-scoped, mirroringservice/<node>/<service>:<node>is the host node;<name>is the vertex's local identifier on that node. By convention the rete-wide mesh-vertex is namedrete, giving e.g.spiffe://rete-lovers/vertex/alpha/retefor alpha's rete vertex. The name is an operator choice, not a validator rule. A future interrete-vertex on the same node would bespiffe://rete-lovers/vertex/alpha/interreteby convention.spiffe://<rete>/control-plane/<name>— the signing authority for ctrl artifacts. Distinct fromnode/(a service-class identity used by agents to fetch artifacts from config-server) and fromservice/config-server(a service-class identity for the coordination service). The CP-signing authority compromises independently of those services and rotates on its own cadence, which is why it has its own kind. Symmetric withmanagement-plane/<name>, which already signs mgmt artifacts in C0. In C0 thecontrol-planekind exists only as a reservation; nothing produces ctrl artifacts there. The mgmt artifact lists authorized CP pubkeys for the rete (see C1's ctrl-plane doc).
Convenience Hostname (services only)
<service>.<rete>.rete # rete-scoped (<rete> = trust domain, may be dotted)
<service>.<node>.<rete>.rete # node-scopedExamples:
api.rete-lovers.retessh.alpha.rete-lovers.rete
The service/ segment is elided in the hostname because services are the only kind of entity a workload dials by name. Users and (in C1) rete vertices have SPIFFE IDs but no hostname form. (Internally, the vertex kind is dialable too and renders to the same shape — flor dials vertices for mesh transit — but operators never type vertex hostnames.)
.rete is the Florete TLD. It's not registered in public DNS; only flor agents resolve it (via an in-process resolver or a split-horizon stub that intercepts queries for .rete and routes them through the rete). This gives .rete hostnames drop-in compatibility with standard tools (ssh, curl, psql, browsers with local resolver) without polluting real DNS.
Resolution is contextual. A .rete hostname is turned into an identity with namespace context, not by self-sufficient parsing. In C0 the context is the caller's rete (its trust domain): an agent strips the known trust-domain suffix, and the residual label count distinguishes rete-scoped (1 label) from node-scoped (2). This is why <rete> may be a dotted trust domain (e.g. rete.local) — there is no single-label requirement, and no short-name↔trust-domain table to maintain. Beyond a single rete, the same step becomes a lookup against a network-aware Name Service. See ADR-0007 for the naming/identity/routing decoupling that makes this work.
Reserved Names
To keep URIs unambiguous:
- Kind segments used in SPIFFE paths are reserved identifiers and cannot be used as node, user, or service names:
user,service,node,vertex,management-plane,control-plane. Undervertex/, the second and third path segments identify the host node and the local vertex name respectively; no sub-labels are reserved (vertex names are operator-chosen, like service names underservice/<node>/). - Node and service names cannot contain
/and cannot collide with each other (a service namedalphawhen there's a nodealphais ambiguous in the node-scoped form). - Labels in hostnames must be DNS-label-valid (lowercase alphanumeric + hyphen, 1–63 chars).