SelfTune Self-Host runs the canonical OSS dashboard and the same Remote Library
v1 protocol used by SelfTune Cloud. It is one non-root container with one
/data volume. SQLite stores users, organizations, roles, snapshot heads,
private shares, and audit records; immutable content-addressed objects live
beside it. Raw transcripts never sync.
Before you start
You need Docker Compose, persistent storage, an HTTPS reverse proxy for public
access, and an administrator who owns authentication, backups, and upgrades.
Start the service
Open http://localhost:8787 and enter the admin token. The container runs as
UID 10001, uses a read-only root filesystem, drops Linux capabilities, and
stores writable state only in selftune-data.
The web Library and Skill Sets screens read the authenticated admin
organization’s Remote Library snapshot. They never inspect a filesystem-local
skill installation inside the container. This hosted dashboard is intentionally
read-only for Skill Sets: modify a Set on a trusted device, sync it, then reload
the self-hosted dashboard.
Put TLS in front before exposing the service publicly. Set
SELFTUNE_PUBLIC_URL to the exact HTTPS origin; SelfTune does not trust the
incoming Host header to create an allowed origin.
Connect a SelfTune installation
Preview the artifact manifest before the first sync. Keep account tokens out of
shell history, skill packages, source control, and diagnostic output.
The sync protocol uploads immutable skill revisions, selected drafts, Skill
Sets with their pinned dependencies, decision history, and redacted evidence
summaries. It does not upload raw prompts or transcripts.
Private accounts and shares
The admin account is configured with SELFTUNE_SELFHOST_ADMIN_EMAIL and
SELFTUNE_AUTH_TOKEN. Add closed accounts with
SELFTUNE_SELFHOST_USERS_JSON:
Restart after changing accounts. Removed accounts are deactivated without
deleting their snapshots or share history. Tokens are SHA-256 hashed in SQLite,
but the .env file still contains the source credentials and must remain
private.
Set SELFTUNE_PACK_LINK_SECRET to a separate stable random value when you use
managed Pack links. If omitted, self-host falls back to SELFTUNE_AUTH_TOKEN;
rotating that token keeps existing links valid but makes their URLs
non-reconstructable in Shared Packs. A dedicated stable Pack secret avoids
that management-only limitation.
Only immutable skill revisions and Skill Sets can be shared. A Skill Set share
includes every pinned skill revision, requires explicit recipient acceptance,
and copies the objects into the recipient organization during import. Senders
can revoke a share and may set an expiration.
For link distribution, Desktop first uploads a sealed portable Skill Set
envelope to the connected self-host, commits its immutable object into a Remote
Library snapshot, then asks POST /api/v1/remote-library/packs for an expiring
Pack. The returned URL uses SELFTUNE_PUBLIC_URL, for example
https://selftune.example.com/p/<opaque-id>. The source account token is used
only for issuance and revocation; recipients need the Pack URL, not the source
account token. Public preview and content endpoints reveal only the sealed Pack
bound to that opaque token. Revocation immediately makes both unavailable.
Authenticated GET /api/v1/remote-library/packs powers Desktop’s Shared
Packs view, including expiry and claim status plus copyable URLs for newly
issued managed links. Only the configured account can list or revoke them.
The branded browser page uses the same metadata-first preview as SelfTune Cloud
and includes an Open in SelfTune Desktop handoff. The handoff carries the
self-host origin and Pack token only. Desktop still rejects it unless that exact
origin is already connected under Sync & Backup.
Desktop accepts a self-host Pack URL only when its origin exactly matches the
server already connected under Sync & Backup. This makes the existing
operator-selected server URL the SSRF trust boundary; arbitrary pasted origins
are rejected before any network request.
Back up and restore
Stop writes and archive the complete named volume:
Restore into an empty selftune-data volume while the service is stopped.
Never archive only selftune-selfhost.db: snapshots reference files under
objects/, so both stores are required for a consistent recovery.
Health and updates
GET /healthz is an unauthenticated process-liveness endpoint. GET /readyz
verifies SQLite initialization and every configured organization’s referenced
immutable objects. Readiness returns 503 when storage is unavailable or
integrity is degraded, and the container health check uses this endpoint.
Neither probe returns tenant data. Every Remote Library operation under
/api/v1/remote-library/* requires a bearer token and applies its persisted
role and organization scope.
Corrupt or missing immutable objects degrade readiness and block affected
snapshot operations rather than returning silently damaged content.
Release builds publish linux/amd64 and linux/arm64 images to
ghcr.io/selftune-dev/selftune-selfhost. Pin SELFTUNE_VERSION in Compose for
controlled upgrades; SQLite migrations run before the server accepts traffic.
Self-hosting changes who operates security and availability; it does not
remove those responsibilities. Do not expose the service without TLS,
authentication, persistent storage, backups, and an upgrade owner.