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.
Your identity keypair and provider secrets live as files 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/hold your identity and provider keys. Keep the directory private (permissions are0600, owner-only) and back it up encrypted.- The record store carries every record. Bearer-token secrets are not stored in it; only a hash is kept for validation.
- Excluded from auto-backup: the startup backup at
~/.local/share/syncropel/backups/instance-<did-tail>/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).
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 the record store as issued. Only a hash lands; the token is shown ONCE at mint time and never persisted in the instance. If you misplace a token, mint a new one.
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 a copy of the record store to ~/.local/share/syncropel/backups/instance-<did-tail>/hub.db.bak automatically on every startup (the directory is keyed by the instance DID — see Operator runbook → Backup discipline). 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 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 the record store)
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 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 your keys. A discarded SSD without secure-erase yields your keys.
Mitigation: full-disk encryption (FileVault on macOS, LUKS on Linux, BitLocker on Windows). Secure-erase before disposal.
Hosted instances
On a hosted instance the operator of the host can access the instance's keys, as with any hosted service; run locally if that is unacceptable. Self-hosting users on shared compute (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 the stored hash |
| 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) |
| A work loop reading the provider credential or the operator's bearer | ✓ (sandboxed transport, the default for every non-operator tier; opt-in for the operator's own runs) | separate isolated process, no network, no view of the instance home, one scoped credential handed to it at start |
| A same-user process reading the daemon's environment or memory | ✓ | the daemon's process is protected and credentials are moved out of its environment at boot |
| A run declaring its own success or its own permission to act | ✓ | the kernel authors the tool verdict and the outcome; the run credential cannot write either kind |
Protection domains and the work loop
Every tool a work loop can reach is classified by protection domain before it is offered, and the domains order: sandboxed < mediated < kernel-trust-domain. A sandboxed worker executes only what its domain allows and asks the kernel for everything else through five doors on a local socket. The daemon checks each isolation layer before it hands the run its credential. The order is the guarantee: a worker that could not be isolated is stopped having never held a credential.
Three things a run cannot say about itself:
- Whether it was allowed to act. Every tool call is judged by the kernel over the live permission rules and recorded as
core.work.tool_verdict.v1before it runs. The run credential cannot write that kind. - Whether it succeeded. The kernel writes the outcome, counts the turns itself, requires a clean exit, and checks the claim against the thread's own tool tally: a run whose every tool call failed is not a success whatever its final answer says. The outcome carries
tool_calls_okandtool_calls_failed. - What tools it has. A defined actor's declared tool list bounds its runs on both transports; a declaration can narrow a tier and never widen one.
Host tools (bash and the filesystem set) are wired for the operator's own machine only. The rollback for the whole transport is one config record; see The sandboxed transport.
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.
The sandboxed transport
Run every work loop in a separate isolated process instead of inside the daemon. How to turn it on and off, what the kernel judges per tool call, the admission ceiling, what changes on your machine, and how to verify it on your instance.