Identity & Naming
Principal types, CA management, and SPIFFE naming scheme for C0
Identity Model & CA
Types of Principal
| Kind | Exists in C0 | Used for |
|---|---|---|
| Cluster CA root | Yes | Signs all other identities |
| User | Yes | Human workload, end-to-end mTLS to services |
| Service | Yes | Service workload, end-to-end mTLS + as caller (principal role) |
| Node | Yes | Control-plane mTLS — node ↔ cluster infrastructure (config-server, metrics) |
Key points:
- Nodes are principals, but only at the control-plane layer. Every node has a SPIFFE identity (
spiffe://<cluster>/nodes/<node>) used solely for node ↔ cluster-infrastructure mTLS — fetching compiled state from the config-server, pushing metrics. 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, with the node's own identity out of the picture. - 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 is a separate kind from the future cluster-vertex identity. C1 introduces
cluster-vertices/for hop-by-hop mesh-transit mTLS. The two kinds stay distinct: a machine can join multiple Florete clusters (one node identity per cluster) and could, in theory, carry more than one cluster vertex per cluster. Whether to merge them is an open design question deferred to C1.
CA Management
- Cluster CA: one root CA per Florete cluster, generated by
florctl ca init. Publicca.crtlives in the git repo; 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/<name>.key). Never in the repo. - Signed certs: delivered to the principal that holds the matching private key; stored locally at
~/.flor/<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 runsflorctl compile→florctl publish. The nextflor 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 cluster 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, SNI, shell command-lines) — the latter exists only for services, since users are not things you curl or ssh to.
Canonical SPIFFE URI
spiffe://<cluster>/users/<user> # user principal
spiffe://<cluster>/services/<service> # cluster-scoped service
spiffe://<cluster>/services/<node>/<service> # node-scoped service
spiffe://<cluster>/nodes/<node> # node principal (control-plane)Examples:
spiffe://rete-lovers/users/alice— user principal.spiffe://rete-lovers/services/api— api service (cluster-scoped).spiffe://rete-lovers/services/alpha/ssh— SSH on node alpha.spiffe://rete-lovers/nodes/alpha— alpha's node principal (used to reach the config-server and metrics services over Florete).
C1 extends this with spiffe://<cluster>/cluster-vertices/<node> for mesh-transit mTLS — a separate kind from nodes/, because a single machine can participate in multiple Florete clusters (one node identity per cluster) and could in theory carry more than one cluster vertex per cluster. Merging the two kinds is a C1+ design question.
Convenience Hostname (services only)
<service>.<cluster>.rete # cluster-scoped
<service>.<node>.<cluster>.rete # node-scopedExamples:
api.rete-lovers.retessh.alpha.rete-lovers.rete
The services/ segment is elided in the hostname because services are the only kind of entity a workload dials by name. Users and (in C1) cluster vertices have SPIFFE IDs but no hostname form.
.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 mesh). This gives .rete hostnames drop-in compatibility with standard tools (ssh, curl, psql, browsers with local resolver) without polluting real DNS.
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:
users,services,nodes,cluster-vertices. - 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).