SSyncropel Docs

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 are 0600 (owner-only).
  • ~/.syncro/hub.db is an unencrypted SQLite carrying every record. Bearer-token plaintext is NOT stored — only sha256(secret) for validation.
  • Excluded from auto-backup: ~/.local/share/syncropel/backups/hub.db.bak does 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 post-v0.31 work tracked as SKL-0641. Until it ships, file-permission discipline is the only at-rest defense.

What's stored where

AssetLocationFormatPermissionEncrypted at rest
Ed25519 identity seed (the DID's signing key)~/.syncro/keys/primaryraw 32-byte seed0600No
Provider API keys (Anthropic, OpenAI, etc.)~/.syncro/secrets/<provider>.keyplaintext0600No
Bearer tokens (issued + cached server-side)~/.syncro/hub.db (tokens table)sha256(secret) only — plaintext NEVER persistedn/a (db)n/a
Operator session token (CLI)~/.syncro/tokenplaintext0600No
Federation pair tokens (peer-issued)~/.syncro/hub.db (tokens table)as above (sha256)n/an/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 doctor

The audit runs against ~/.syncro/secrets/ and ~/.syncro/keys/. A failed audit returns exit code 1 and prints the offending paths.

Token-at-rest format (ADR-035 D6)

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 daemon. If you misplace a token, mint a new one — no recovery from hub.db.

Per-daemon keypair scope

The identity keypair is per-daemon, 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 --daemon 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 daemon'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 daemon'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 serve --daemon

Accidental 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/token

Forensic 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 Fly Machine runs spl serve with the daemon's identity in the container's filesystem. The Cloudflare provisioning Worker (per ADR-067) provisions 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 Fly's volume encryption + Cloudflare's auth-proxy isolation per the production architecture. 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

Per SEC-007 (resolved 2026-05-04 via SKL-0660): 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

ThreatProtected?By
Casual file enumeration on multi-user host0600 file permissions
Daemon-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 tokenServer-side validation against sha256(secret) lookup
Host root / daemon-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: hosted-instance platform isolation)
Audit gap on portability opsth_audit_portability (since v0.31)

What's coming post-v0.31

OS-keyring integration (SKL-0641, post-v0.31): integrate 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 for v0.32 ship vs later: spec implications around key rotation semantics, backup/restore round-trip behavior, and federation pair re-establishment after key rotation are non-trivial. v0.31 ships with this documentation; SKL-0641 ships when the spec questions resolve.

See also

On this page