Data Flows#
Audience: Operations Administrators, Security Professionals
Prerequisites: Understanding of HTTP and security concepts
Outcome: Understand how data flows through the system for common operations
Overview#
Kleidia uses a frontend-mediated architecture where the browser orchestrates operations between cloud services and local agents. All sensitive operations are encrypted using RSA-OAEP encryption before transmission.
Authentication Flow#
User Login#
┌─────────┐ ┌──────────┐ ┌────────────┐
│ Browser │────────▶│ Backend │────────▶│ PostgreSQL │
│ │ HTTPS │ │ │ │
└─────────┘ └──────────┘ └────────────┘
│ │
│ │ Validate credentials
│ │ Generate JWT token
│◀───────────────────│
│ JWT token │
│ │
│ Store token │
│ Redirect to dashboardSteps:
- User enters credentials in browser
- Frontend sends
POST /api/auth/loginto backend - Backend queries PostgreSQL to validate credentials
- Backend generates JWT token with user ID and session info
- Backend returns JWT token to frontend
- Frontend stores token and redirects to dashboard
Data Transmitted:
- Credentials (HTTPS encrypted)
- JWT token (contains user ID, session ID, expiration)
Agent Registration Flow#
Agent Discovery and Key Exchange#
┌─────────┐ ┌──────────┐ ┌────────────┐
│ Browser │────────▶│ Agent │ │ Backend │
│ │ HTTP │ localhost│ │ │
└─────────┘ └──────────┘ └────────────┘
│ │ │
│ GET /.well-known │ │
│◀───────────────────│ │
│ Agent status │ │
│ │ │
│ GET /pubkey │ │
│◀───────────────────│ │
│ Public key (PEM) │ │
│ │ │
│ POST /api/session/ │ │
│ {id}/register-agent│ │
│────────────────────┼───────────────────▶│
│ │ │ Store in PostgreSQL
│◀───────────────────┼─────────────-──────│
│ Success │ │Steps:
- User logs in → Frontend detects agent
- Frontend calls
GET http://127.0.0.1:56123/.well-known/kleidia-agent - Agent responds with status and version
- Frontend calls
GET http://127.0.0.1:56123/pubkey - Agent returns ephemeral RSA public key
- Frontend sends public key to backend:
POST /api/session/{id}/register-agent - Backend stores public key in
user_sessions.agent_pubkey - Backend confirms registration
Data Transmitted:
- Agent public key (PEM format)
- Session ID (for binding)
YubiKey Operation Flow#
PIN Change Operation#
┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Browser │────────▶│ Backend │────────▶│ OpenBao │ │ Agent │
│ │ HTTPS │ │ │ (Vault) │ │ localhost│
└─────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │ │
│ GET /api/yubikey/ │ │ │
│ {serial}/secrets │ │ │
│───────────────────▶│ │ │
│ │ Get agent_pubkey │ │
│ │ from PostgreSQL │ │
│ │ │ │
│ │ Get PIN from Vault │ │
│ │───────────────────▶│ │
│ │◀───────────────────│ │
│ │ PIN (plaintext) │ │
│ │ │ │
│ │ Encrypt PIN with │ │
│ │ agent public key │ │
│ │ (RSA-OAEP) │ │
│◀───────────────────│ │ │
│ Encrypted PIN │ │ │
│ │ │ │
│ POST /piv/set-pin │ │ │
│ {encrypted: true, │ │ │
│ pin: "..."} │ │ │
│─────────────────────────────────────────────────────────────▶│
│ │ │ │
│ │ │ Decrypt PIN │
│ │ │ Execute ykman │
│ │ │ piv change-pin │
│◀─────────────────────────────────────────────────────────────│
│ Success │ │ │Steps:
- User requests PIN change in frontend
- Frontend requests secrets:
GET /api/yubikey/{serial}/secrets - Backend retrieves agent public key from PostgreSQL
- Backend retrieves PIN from OpenBao Vault
- Backend encrypts PIN using agent’s RSA public key (RSA-OAEP)
- Backend returns encrypted PIN to frontend
- Frontend sends encrypted PIN to agent:
POST http://127.0.0.1:56123/piv/set-pin - Agent decrypts PIN using private key
- Agent executes
ykman piv change-pincommand - Agent returns success/failure to frontend
- Frontend updates UI with result
Data Transmitted:
- Encrypted PIN (RSA-OAEP encrypted)
- Serial number (plaintext)
- Operation result (success/failure)
Certificate Generation Flow#
CSR Generation and Certificate Signing#
┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Browser │────────▶│ Agent │ │ Backend │────────▶│ OpenBao │
│ │ HTTP │ localhost│ │ │ │ (Vault) │
└─────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │ │
│ POST /piv/ │ │ │
│ generate-csr │ │ │
│───────────────────▶│ │ │
│ │ Generate CSR using │ │
│ │ YubiKey private key│ │
│◀───────────────────│ │ │
│ CSR (PEM) │ │ │
│ │ │ │
│ POST /api/yubikey/ │ │ │
│ {serial}/sign-csr │ │ │
│────────────────────┼───────────────────▶│ │
│ │ │ Sign CSR via PKI │
│ │ │───────────────────▶│
│ │ │◀───────────────────│
│ │ │ Signed certificate │
│◀───────────────────┼─────────────-──────│ │
│ Certificate (PEM) │ │ │
│ │ │ │
│ POST /piv/ │ │ │
│ import-certificate │ │ │
│───────────────────▶│ │ │
│ │ Import certificate │ │
│ │ to YubiKey PIV slot│ │
│◀───────────────────│ │ │
│ Success │ │ │Steps:
- User requests certificate generation
- Frontend calls agent:
POST http://127.0.0.1:56123/piv/generate-csr - Agent generates CSR using YubiKey’s private key (slot 9a)
- Agent returns CSR to frontend
- Frontend sends CSR to backend:
POST /api/yubikey/{serial}/sign-csr - Backend retrieves management key from Vault
- Backend signs CSR using OpenBao PKI engine
- Backend returns signed certificate to frontend
- Frontend sends certificate to agent:
POST http://127.0.0.1:56123/piv/import-certificate - Agent imports certificate to YubiKey PIV slot
- Agent returns success to frontend
Data Transmitted:
- CSR (Certificate Signing Request, PEM format)
- Signed certificate (PEM format)
- Serial number (plaintext)
Secret Storage Flow#
Storing YubiKey Secrets in Vault#
┌─────────┐ ┌──────────┐ ┌──────────┐
│ Browser │────────▶│ Backend │────────▶│ OpenBao │
│ │ HTTPS │ │ │ (Vault) │
└─────────┘ └──────────┘ └──────────┘
│ │ │
│ POST /api/yubikey/ │ │
│ {serial}/secrets │ │
│───────────────────▶│ │
│ │ Store in KV v2 │
│ │ yubikeys/data/ │
│ │ {serial}/secrets │
│ │───────────────────▶│
│ │◀───────────────────│
│ │ Success │
│◀───────────────────│ │
│ Success │ │Steps:
- Admin registers YubiKey and provides PIN/PUK/management key
- Frontend sends secrets to backend:
POST /api/yubikey/{serial}/secrets - Backend encrypts secrets (if needed) and stores in Vault
- Backend stores at path:
yubikeys/data/{serial}/secrets - Vault encrypts data at rest
- Backend confirms storage
Data Stored:
- PIN (encrypted at rest by Vault)
- PUK (encrypted at rest by Vault)
- Management key (encrypted at rest by Vault)
Security Considerations#
Encryption Layers#
- HTTPS/TLS: All browser-to-backend communication encrypted
- RSA-OAEP: Sensitive data encrypted with agent public key before transmission
- Vault Encryption: Secrets encrypted at rest in Vault
- Database Encryption: PostgreSQL data encrypted at rest (if configured)
Data Never Transmitted in Plaintext#
- PINs: Always encrypted with RSA-OAEP before transmission
- PUKs: Always encrypted with RSA-OAEP before transmission
- Management Keys: Always encrypted with RSA-OAEP before transmission
- Private Keys: Never leave YubiKey hardware
Session Binding#
- Agent public keys stored in
user_sessionstable - Keys expire when user session expires
- Zero standing access for logged-out users