Deevnet Secure Identity Standard #
Purpose #
This document defines what it means for Deevnet client access to be secure.
Secure identity is not defined by whether you can “SSH in,” but whether access:
- minimizes secret exposure,
- is reproducible across devices and OSes,
- supports least privilege and revocation,
- and avoids habits that don’t scale beyond a lab.
This document captures the intent, principles, and invariants of secure client identity and access in Deevnet.
1. Foundational Principles #
1.1 Identity Is Long-Lived; Credentials Are Short-Lived #
Clients MAY hold long-lived identity material (e.g., SSH private keys).
Everything else should trend short-lived:
- short-lived certificates over static keys (where feasible)
- short-lived tokens over static API keys
- secrets fetched just-in-time over secrets stored on disk
If compromise of a single laptop exposes long-lived secrets, the design is incorrect.
1.2 Clients Hold Identity, Not Secrets #
A client device is allowed to prove who you are — it should not permanently store what you know.
Allowed on clients
- SSH keys and agent state
- OS-backed secure storage (Keychain / Credential Manager)
- configuration that references secret locations (paths), not secret values
Incorrect on clients
.envfiles with real credentials- plaintext secrets in scripts, notes, or repos
- long-lived API keys copied around “for convenience”
1.3 No Copy/Paste Secrets as a Workflow #
Copy/paste is a leakage engine.
- it creates untracked exposure
- it normalizes unsafe habits
- it leads to shell history, scrollback, and logging leaks
If a workflow requires manually pasting secret material, it is considered incorrect.
1.4 Names Over Addresses (Client Access) #
Hostnames are the interface; IPs are plumbing.
- Clients MUST prefer DNS names to avoid per-host client configuration drift
- If an IP changes and clients must be edited, correctness is violated
Example concept (DNS-driven access):
Host dv02*
HostName %h.mobile.deevnet.net
2. Identity vs Access vs Secrets (Separation of Concerns) #
2.1 Identity #
Identity answers: who is this user/device?
Examples:
- SSH keypair (client identity)
- hardware-backed key storage (where applicable)
2.2 Access #
Access answers: what systems may this identity reach and what may it do?
Examples:
- SSH authorization on hosts
- bastion / jump paths
- role-based privileges on target systems
2.3 Secrets Delivery #
Secrets delivery answers: how do tools/apps obtain sensitive values without persisting them?
Examples:
- session-scoped injection
- broker-issued short-lived tokens
- agent-based delivery
If one layer must “reach across” to do another layer’s job, the design is incorrect.
3. SSH Identity Invariants #
3.1 Private Keys Do Not Move #
Private keys MUST remain on the client.
- use SSH agents
- use forwarding only to systems you control and trust
- prefer short-lived credentials when available
Example concept:
Host *.mobile.deevnet.net
ForwardAgent yes
3.2 SSH Config Is Declarative and Compact #
Client access configuration MUST be:
- declarative
- reproducible
- OS-portable where possible
Avoid “one host = one stanza” unless you need a true exception.
Example concept (wildcards + shared settings):
Host dv02* dv00*
User cdeever
IdentityFile ~/.ssh/id_ed25519
ForwardAgent yes
4. Secrets Handling Invariants #
4.1 Environment Variables Are Delivery, Not Storage #
Environment variables MAY be used to deliver secrets to a process, but SHOULD NOT become a manual storage method.
Incorrect:
export DB_PASSWORD="paste-secret-here"
Correct concept: secrets are injected ephemerally for a single process or subshell (broker is a placeholder):
broker exec apps/foo -- make deploy
4.2 Secrets Must Be Session-Scoped (or Process-Scoped) #
Secrets should exist:
- only for the duration of a single command, or
- only inside a short-lived subshell
If secrets persist across terminal sessions by default, the design is incorrect.
4.3 No Secrets in Infrastructure-as-Code #
IaC MUST reference secret locations, not secret values.
Incorrect:
db_password: "hunter2"
Correct concept:
db_password_path: "secret/apps/foo/db_password"
4.4 IaC Source and IaC State Are Different Things #
§4.3 governs IaC source — the .tf, .tfvars and playbook files a human writes. Terraform
state is not source. It is the record of an apply, written by the tool, and it can contain
attribute values that were never typed into any file.
That distinction matters because the protection is different in kind. Source is protected by review:
a secret in a .tf is visible in a diff. State is protected by nothing — it is machine-generated,
rarely read, and a provider that decides to persist an attribute will do so silently on the next
apply.
The invariant:
The moment state contains a secret value, whatever holds that state becomes a secret store.
So a repository holding state is a secret store, and a bucket holding state is a secret store, and both must be treated as one. Two rules follow:
- Prefer resources whose values can be re-derived. A declaration that generates a credential puts that credential in state permanently. Issue credentials out of band and reference them.
- Do not assume state is clean because it is clean today. Whether a given provider persists a sensitive attribute is that provider’s implementation detail, and it can change under a version bump with nothing in the configuration changing at all.
Where Deevnet’s own state lives, and what enforces this, is ADR-0007.
5. Definition of “Secure” (for Deevnet Client Access) #
Deevnet client access is secure when:
- SSH access works using DNS names without per-host client edits
- private keys remain on the client; authentication uses agents/certs
- secrets are not pasted, exported manually, or stored in plaintext files
- IaC contains references to secrets, not the secrets themselves
- access can be revoked without hunting through laptops and scripts
- the “right way” is the easiest way
If secure access is the default path of least resistance, the system is secure.