Seif
Seif de credențiale zero-knowledge: sincronizare criptată, broker pentru agenți, aprobări și chei de dispozitiv.
codai Vault îți păstrează credențialele criptate pe propriile dispozitive: clienții sigilează fiecare element (XChaCha20-Poly1305, chei împachetate cu hibridul post-cuantic X-Wing) înainte de încărcare, deci /v1/vault/* stochează și sincronizează doar text cifrat. Agenții nu primesc niciodată valori secrete. Ei văd un catalog de handle-uri ({{vault:<slug>}}), cer o decizie de politică (niveluri t0-t4 sau deny), așteaptă aprobarea ta când nivelul o cere și apelează endpoint-uri HTTPS intermediate al căror răspuns este redactat.
SDK-urile expun toată suprafața ca codai.vault (TypeScript și Python), iar cheile de dispozitiv sub codai.vault.devices.
Chei de dispozitiv și confirmare dovedită
O aprobare dovedește cine a decis. Pe lângă semnătura v1 a cheii seifului, o decizie poate purta o dovadă v2 de la o cheie de dispozitiv protejată hardware (Android Keystore pe telefon și ceas, Windows Hello pe desktop) sau de la un passkey. Cheile de dispozitiv sunt doar chei publice; cheia privată nu părăsește niciodată hardware-ul securizat al dispozitivului.
Înrolarea
POST /v1/vault/devices/challengeîntoarce unnoncede unică folosință (32 de octeți aleatori, base64url), valabil 5 minute.- Dispozitivul își creează cheia, iar cheia seifului și cheia dispozitivului semnează amândouă mesajul de înrolare:
codai-vault/device-enroll/v1|<device_id>|<kind>|<spki_sha256>|<nonce>, undespki_sha256este base64url(SHA-256(SubjectPublicKeyInfo DER)). POST /v1/vault/devicestrimitedevice_id(android-…,wear-…saudesktop-…),kind(android_keystoresauwindows_hello),name,surface(phone,wearsaudesktop),public_key,nonce,vault_signature,device_signatureși, când există, oattestation. Răspunsul este201 { device }.
Reînrolarea aceluiași device_id îi înlocuiește cheia. GET /v1/vault/devices listează cheile tale (inclusiv cele revocate), PATCH /v1/vault/devices/{id} redenumește una, iar DELETE /v1/vault/devices/{id} o revocă imediat: următoarea ei dovadă primește 403 device_revoked.
Toate valorile binare sunt base64url fără padding. Android semnează cu ECDSA P-256 SHA-256 (DER); Windows Hello semnează cu RSASSA-PKCS1-v1_5 SHA-256.
Tipuri de atestare
attestation | Semnificație |
|---|---|
android-strongbox | Atestarea Google a cheii verificată, cheia ținută într-un element securizat StrongBox. |
android-tee | Atestarea Google a cheii verificată, cheia ținută în TEE. |
tpm | O atestare TPM a cheii Windows Hello, verificată față de rădăcinile TPM Microsoft. |
hello-unattested | O cheie Windows Hello fără atestare TPM. |
Cheile Android trebuie să trimită attestation: { android_chain: [...] } (frunza prima); provocarea de atestare este formată din octeții nonce-ului, iar cheia atestată trebuie să fie egală cu public_key. Cheile Windows Hello pot trimite attestation: { tpm: { attestation, cert_chain } }; fără ea cheia este hello-unattested, cu una invalidă înrolarea eșuează. Erori: 400 invalid_nonce, possession_failed, attestation_required, attestation_invalid; 403 invalid_signature; 409 device_id_taken; 503 revocation_unavailable (lista de revocare Google nu a putut fi verificată, reîncearcă mai târziu).
Mesajele de aprobare
- v1 (cheia seifului):
codai-vault/approve/v1|<approval_id>|<action_hash>|<decision>|<scope>, trimis casignature. - v2 (cheie de dispozitiv sau passkey):
codai-vault/approve/v2|<approval_id>|<action_hash>|<decision>|<scope>|<device_id>, trimis caproof: { type: 'device', device_id, signature }. - v3 (cheie de dispozitiv sau passkey + potrivirea numărului):
codai-vault/approve/v3|<approval_id>|<action_hash>|<decision>|<scope>|<device_id>|<preview_digest>|<match_number>, trimis cumatch_numberlângăproof.preview_digesteste base64url(SHA-256) al textului canonic alrequest.preview(codai-vault/preview/v1, apoititle,agent,mode,host,action,purpose,taskca liniicheie=valoare, cu\, linie nouă și CR escapate), deci semnătura acoperă exact ce a văzut aprobatorul. - passkey: provocarea WebAuthn este base64url(SHA-256(mesajul v2 cu id-ul de dispozitiv
passkey)), trimisă caproof: { type: 'passkey', response }.
Trimite cel mult unul dintre signature și proof. step_up_acr este încă acceptat, dar ignorat: o confirmare declarată de client nu dovedește nimic.
Reguli pe niveluri
- Refuzul nu cere dovadă (butonul Refuză din notificare este suficient).
- Aprobarea T3 acceptă o semnătură v1 validă, o dovadă de dispozitiv sau o dovadă passkey.
- Aprobarea T4 cere o dovadă de dispozitiv sau passkey; o semnătură v1 primește 403
step_up_required. Toate tipurile de atestare de mai sus sunt acceptate astăzi. - Ceasul Wear aprobă doar T3: o cheie de ceas nu poate fi atestată ca legată de biometrie, deci o dovadă de la un dispozitiv
wearpe o aprobare T4 primește 403wear_t3_only. - Potrivirea numărului: fiecare cerere T3/T4 primește un
match_numberdin 2 cifre, întors doar agentului (/vault/use,/vault/http,/vault/resolve). Aprobatorul vederequest.previewși treirequest.match_choices, niciodată numărul. O aprobare T4 pe o astfel de cerere cerematch_numbercu o dovadă v3: fără număr primește 403match_required; un număr greșit refuză imediat cererea și primește 403match_mismatch. Aprobările T3 și refuzurile acceptă în continuare v1/v2.
Alte erori de dovadă (403): invalid_signature, unknown_device, device_revoked, passkey_invalid.
bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
OK.
application/json- response
nonce*string32 de octeți aleatori, base64url. Se folosește o singură dată.
expires_at*stringLa 5 minute după emitere.
date-timecurl -X POST 'https://ai.codai.ro/v1/vault/devices/challenge'{ "nonce": "string", "expires_at": "2019-08-24T14:15:22Z"}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
application/json- body
device_id*string^(android|wear|desktop)-[A-Za-z0-9._-]{4,80}$kind*string"android_keystore""windows_hello"name*string1 <= length <= 80surface*string"phone""wear""desktop"public_key*stringSubjectPublicKeyInfo DER al cheii dispozitivului, base64url.
nonce*stringNonce-ul din POST /v1/vault/devices/challenge.
vault_signature*stringSemnătura Ed25519 a cheii seifului peste codai-vault/device-enroll/v1|<device_id>|<kind>|<spki_sha256>|<nonce>.
device_signature*stringCheia dispozitivului peste același mesaj (ECDSA P-256 DER pe Android, RSASSA-PKCS1-v1_5 SHA-256 pentru Windows Hello), base64url.
attestation?|Obligatorie pentru android_keystore (lanț de certificate, frunza prima, provocarea de atestare = octeții nonce-ului). Opțională pentru windows_hello: o atestare TPM dă tpm, lipsa ei dă hello-unattested.
Înrolat.
application/json- response
device*curl -X POST 'https://ai.codai.ro/v1/vault/devices' \ -H 'Content-Type: application/json' \ -d '{ "device_id": "string", "kind": "android_keystore", "name": "string", "surface": "phone", "public_key": "string", "nonce": "string", "vault_signature": "string", "device_signature": "string"}'{ "device": { "id": "string", "kind": "android_keystore", "name": "string", "surface": "phone", "public_key": "string", "attestation": "android-strongbox", "attestation_info": {}, "created_at": "2019-08-24T14:15:22Z", "last_used_at": "2019-08-24T14:15:22Z", "revoked_at": "2019-08-24T14:15:22Z" }}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
OK.
application/json- response
devices*array<>curl -X GET 'https://ai.codai.ro/v1/vault/devices'{ "devices": [ { "id": "string", "kind": "android_keystore", "name": "string", "surface": "phone", "public_key": "string", "attestation": "android-strongbox", "attestation_info": {}, "created_at": "2019-08-24T14:15:22Z", "last_used_at": "2019-08-24T14:15:22Z", "revoked_at": "2019-08-24T14:15:22Z" } ]}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringId-ul dispozitivului.
^(android|wear|desktop)-[A-Za-z0-9._-]{4,80}$application/json- body
name*string1 <= length <= 80OK.
application/json- response
device*curl -X PATCH 'https://ai.codai.ro/v1/vault/devices/string' \ -H 'Content-Type: application/json' \ -d '{ "name": "string"}'{ "device": { "id": "string", "kind": "android_keystore", "name": "string", "surface": "phone", "public_key": "string", "attestation": "android-strongbox", "attestation_info": {}, "created_at": "2019-08-24T14:15:22Z", "last_used_at": "2019-08-24T14:15:22Z", "revoked_at": "2019-08-24T14:15:22Z" }}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringId-ul dispozitivului.
^(android|wear|desktop)-[A-Za-z0-9._-]{4,80}$Revocat.
application/json- response
ok*booleancurl -X DELETE 'https://ai.codai.ro/v1/vault/devices/string'{ "ok": true}bearerAuthAutorizareBearer <codai_…>O cheie API codai (prefix codai_). Tokenurile efemere de la POST /v1/tokens sunt acceptate doar de suprafețele media potrivite scope-ului lor (/v1/realtime, /v1/audio/*, /v1/embeddings).
id*stringId-ul resursei (uuid).
uuidapplication/json- body
decision*string"approve""deny"scope*string"once""task""1h""8h"signature?stringSemnătura Ed25519 v1 a cheii seifului, base64url.
proof?|Confirmare dovedită pentru o decizie. device: o cheie de dispozitiv înrolată semnează codai-vault/approve/v2|<approval_id>|<action_hash>|<decision>|<scope>|<device_id>. passkey: o aserțiune WebAuthn a cărei provocare este base64url(SHA-256(același mesaj cu id-ul de dispozitiv passkey)). Cu match_number mesajul este v3: codai-vault/approve/v3|<approval_id>|<action_hash>|<decision>|<scope>|<device_id>|<preview_digest>|<match_number>, unde preview_digest = base64url(SHA-256(textul canonic al request.preview)).
step_up_acr?stringDepreciatIgnorat: o confirmare declarată de client nu dovedește nimic. Folosește proof.
via?string"hub""desktop""phone""wear""extension"match_number?stringNumărul apăsat de utilizator (unul din request.match_choices); cere o dovadă de dispozitiv sau passkey peste mesajul v3.
^[0-9]{2}$OK.
application/json- response
[key: string]?anycurl -X POST 'https://ai.codai.ro/v1/vault/approvals/497f6eca-6276-4993-bfeb-53cbbbba6f08/decide' \ -H 'Content-Type: application/json' \ -d '{ "decision": "approve", "scope": "once"}'{}