Security model — secrets at rest, threat model, operator discipline
What spl serve protects against (default-secure auth + filesystem permissions) and what it does NOT (host compromise, cloud-sync of identity dirs). Read before deploying to a multi-tenant or shared-storage environment.
Identity keypair + provider secrets are stored in plaintext on disk. Filesystem permissions (0600, owner-only) protect against casual file enumeration. They do NOT protect against host compromise, cloud-sync to Dropbox / Time Machine / rsync, forensic disk imaging, or accidental commit to a dotfiles repo. Operators must maintain the discipline below.
TL;DR
~/.syncro/keys/and~/.syncro/secrets/contain plaintext identity + provider keys. Permissions are0600(owner-only).~/.syncro/hub.dbis an unencrypted SQLite carrying every record. Bearer-token plaintext is NOT stored — onlysha256(secret)for validation.- Excluded from auto-backup:
~/.local/share/syncropel/backups/hub.db.bakdoes NOT include the keys/ or secrets/ dirs. - What you must do: exclude
~/.syncro/keys/and~/.syncro/secrets/from cloud-sync (Dropbox, iCloud, OneDrive, rsync, dotfiles repos). On hosted instances: rely on the platform's secret manager rather than mounting~/.syncro/. - What's coming: OS-keyring integration (macOS Keychain, Linux Secret Service, Windows Credential Manager) is planned for a future release. Until it ships, file-permission discipline is the only at-rest defense.
What's stored where
| Asset | Location | Format | Permission | Encrypted at rest |
|---|---|---|---|---|
| Ed25519 identity seed (the DID's signing key) | ~/.syncro/keys/primary | raw 32-byte seed | 0600 | No |
| Provider API keys (Anthropic, OpenAI, etc.) | ~/.syncro/secrets/<provider>.key | plaintext | 0600 | No |
| Bearer tokens (issued + cached server-side) | ~/.syncro/hub.db (tokens table) | sha256(secret) only — plaintext NEVER persisted | n/a (db) | n/a |
| Operator session token (CLI) | ~/.syncro/token | plaintext | 0600 | No |
| Federation pair tokens (peer-issued) | ~/.syncro/hub.db (tokens table) | as above (sha256) | n/a | n/a |
What's protected
Filesystem permissions
All sensitive files land at 0600 (owner-only). spl doctor audits permissions on every run and warns if a file is world-readable:
spl doctorThe audit runs against ~/.syncro/secrets/ and ~/.syncro/keys/. A failed audit returns exit code 1 and prints the offending paths.
Token-at-rest format
Bearer tokens are NEVER stored in plaintext in hub.db. Only sha256(secret) lands; the plaintext is shown ONCE at mint time and never persisted in the instance. If you misplace a token, mint a new one — no recovery from hub.db.
Per-instance keypair scope
The identity keypair is per-instance, content-addressed via did:key. Rotating the keypair (regenerating the DID) invalidates all federation pairs — peers cache the DID and reject pair traffic from a different keypair. Recovery: re-run spl federation pair after rotation.
Bootstrap discipline
spl serve writes hub.db to ~/.local/share/syncropel/backups/hub.db.bak automatically on every startup. This backup deliberately does NOT include ~/.syncro/keys/ or ~/.syncro/secrets/ so a cp of the backup (e.g., to a different host for inspection) doesn't accidentally include identity material.
What's NOT protected (operator must mitigate)
Host compromise
Root or the instance's user account can read every file in ~/.syncro/. The plaintext identity seed lets an attacker:
- Forge records under your DID
- Decrypt federation pair traffic addressed to you
- Issue bearer tokens that pass the instance's auth gate (after replacing
hub.db)
Mitigation: standard host hardening (disk encryption at the filesystem layer, restrict shell access, monitor for unauthorized SSH). For high-stakes tenants, run spl serve inside a dedicated VM or container with the keys/secrets dir on a separate filesystem volume.
Cloud-sync of ~/.syncro/
Dropbox, iCloud, OneDrive, Time Machine, rsync-to-NAS — all happily pick up ~/.syncro/keys/ and ~/.syncro/secrets/ by default. Once the contents are on a sync provider's servers:
- The provider's logs may retain them indefinitely.
- A subpoena to the provider yields your plaintext keys.
- A breach of the provider's storage exposes them.
Mitigation: explicitly exclude these paths from any sync tool you use. Examples:
# rsync — exclude pattern
rsync --exclude='.syncro/keys/' --exclude='.syncro/secrets/' ...
# Time Machine (macOS)
sudo tmutil addexclusion ~/.syncro/keys
sudo tmutil addexclusion ~/.syncro/secrets
# Dropbox / iCloud / OneDrive — install spl OUTSIDE the sync root,
# or use a non-default data dir:
SYNCROPEL_HOME=/var/lib/syncropel spl serveAccidental commit to a dotfiles repo
Some operators commit ~/.syncro/config.toml to a dotfiles repo. The default .gitignore may NOT exclude keys/ and secrets/ — re-check yours.
Mitigation: explicitly add to .gitignore:
.syncro/keys/
.syncro/secrets/
.syncro/tokenForensic disk imaging
A stolen laptop without full-disk encryption yields plaintext keys. A discarded SSD without secure-erase yields plaintext keys.
Mitigation: full-disk encryption (FileVault on macOS, LUKS on Linux, BitLocker on Windows). Secure-erase before disposal.
Hosted instance multi-tenancy
A *.syncropel.com hosted instance runs spl serve with the instance's identity in the container's filesystem. Syncropic's hosted-instance provisioning generates a fresh keypair per tenant, but the container's host sees the keys in plaintext while the container is running.
Mitigation: hosted-instance operators rely on the platform's volume encryption and tenant isolation. Self-hosting users on shared compute (e.g., a VM where another tenant has root) should treat the deployment as compromised by default.
Audit-emission completeness for portability
Since v0.31, every export/import operation emits a core.portability.event.v1 LEARN record on th_audit_portability. Operators auditing "who exported actor X when" or "who initiated instance import" fold this thread:
spl thread records th_audit_portability -o json | jq '.[] | .body'This audit thread does NOT prevent the export from happening — auth scope (Admin for instance-level export, RecordsRead for GET /v1/actors/*/export) is what gates access. The audit makes consequential ops forensically traceable.
Threat model summary
| Threat | Protected? | By |
|---|---|---|
| Casual file enumeration on multi-user host | ✓ | 0600 file permissions |
| Instance-user-only attacker (no root) | ✓ | 0600 file permissions |
| Network-only attacker (over HTTP) | ✓ | Bearer-token auth (default-secure since v0.16) |
| Replay of leaked bearer token | ✓ | Server-side validation against sha256(secret) lookup |
| Host root / instance-user compromise | ✗ | (mitigation: host hardening, full-disk encryption) |
| Cloud-sync of identity dir | ✗ | (mitigation: explicit exclusion) |
| Forensic disk imaging | ✗ | (mitigation: full-disk encryption) |
| Accidental git commit | ✗ | (mitigation: .gitignore) |
| Cross-tenant on shared compute | ✗ | (mitigation: platform-level tenant isolation) |
| Audit gap on portability ops | ✓ | th_audit_portability (since v0.31) |
What's on the roadmap
OS-keyring integration: planned support for the keyring Rust crate to optionally store identity keypair + provider keys in OS keyring services:
- macOS: Keychain
- Linux (with DBus): Secret Service (
gnome-keyring,kwallet) - Windows: Credential Manager
The integration will be opt-in and backwards-compatible — file-based fallback continues to work for headless/server contexts where no keyring service is available.
Decision criteria: spec implications around key rotation semantics, backup/restore round-trip behavior, and federation pair re-establishment after key rotation are non-trivial. The integration ships when the spec questions resolve.
See also
- Authentication & Service Accounts — bearer-token model + scope hierarchy
- Operator runbook — backup discipline — what's backed up + restore procedure
- Actor portability —
spl export/spl importmechanics spl doctor— automated permission audit
Authentication & Service Accounts
Enable bearer-token authentication, create service accounts, pair devices, and manage token lifecycle. Bearer-token auth is enforced by default on every spl serve instance.
Actor portability
Export an actor's identity + work trail from one Syncropel instance and import it into another. What migrates, what doesn't, and the consent implications.