General Architecture
A bird's-eye view of how the sos-vault appliance is put together. This is reference material for operators evaluating, integrating, or auditing the appliance.
Container layout
Everything ships as docker compose services in
/opt/sos-vault/docker-compose.yml:
- nginx — TLS termination, reverse proxy to php-fpm.
Mounts the cert directory from the host. Reloaded by
sysadmin/cert-helperon a new cert install. - app — PHP 8.4 / Laravel 12 / Filament 4 / Livewire 3. Runs the web tier, queue workers, and scheduler.
- db — persistent data store. Schema managed via
php artisan migrate. - redis — cache + queue + session store.
- ollama (optional) — local LLM for the bot. Skippable during install.
Storage
- ZFS pool
sosvaultmounted at/vault. Compressionlz4,atime=off,xattr=sa,ashift=12. The pool can grow viazpool addfrom the Disk Manager page. - System trust store at
/usr/local/share/ca-certificates/— corporate root CAs land here via the Certificate Manager page. - App-level encrypted storage under the vault for case artifacts.
Key management
- Master vault key: GPG keyring named
svault0held in the kernel keyring at runtime. The keyring's master passphrase is collected by the installer in step 5 and stored encrypted in the Wavesettingstable; it is never written to.env. - License verification keyring:
config('license.gpg_home_verify'). Holds the SaaS-side public half only — signing keys never leave the SaaS build host. - Support recipient keyring: the same verify keyring
doubles as the recipient for
sos-vault:capture-server-reportoutput. Encryption is--trust-model alwaysbecause the appliance keyring has no signed trust chain.
Licensing
- Licenses are GPG-signed JSON payloads. The appliance verifies via
LicenseGeneratorService::verify()against the public keyring described above. - Each license binds to one or more machine tokens. The primary token
is
sha256(/etc/machine-id). The appliance refuses to install a.licwhose token set does not include the live host's token. - Seat enforcement is in
User::creating(): refuse a new user when noLocalLicenseis active, or whenUser::count() >= seats. - Renewals extend from the previous expiry, not from "now" — renewing early never loses you days.
Privileged helpers
The PHP tier never invokes zpool, openssl against
live cert paths, or docker exec directly. Every privileged verb
goes through a wrapper script under sysadmin/:
sysadmin/zfs-helper—status,capacity,add,mount-nas,umount-nas.sysadmin/cert-helper—inspect,install,install-corp-ca,reload.sysadmin/init.sh— one-shot GPG keyring bootstrap on install.
Each helper is scoped by a narrow /etc/sudoers.d/ fragment
that grants www-data NOPASSWD on only the verbs the
helper invokes and nothing else.
What leaves the appliance
- Outbound HTTPS to the Customer Portal for module downloads (operator-initiated).
- Optional outbound HTTPS for LLM model pulls.
- Nothing for licensing. Nothing for telemetry. The
sos-vault:capture-server-reportcommand produces an encrypted file for support — you choose if and when to send it.
Branch model
The codebase ships two git branches. master is the SaaS
build; appliance differs by one line in
config/product.php ('saas' →
'appliance'). All gating logic lives on master and propagates
to appliance via merge. Runtime guards check
config('product.type') rather than environment variables, so
tests can flip product type per case.