Self-hosted, AI-powered voice notes and tasks
2.0K
Hit record and say what's on your mind. Skribe works out whether it was a task — with a real due date, a priority and a project — or a note to be tidied into structured markdown, and files it in the right place.
Nothing is written until you accept it. The pipeline produces a proposal that sits on the recording until a human says yes.
Everything runs on your own machine, against your own database, pointed at whichever AI provider you choose — including a model running on the same box.
| Tag | What it is |
|---|---|
latest | The head of main. Moves. |
sha-<short> | One commit, immutable. What to pin, and what to roll back to. |
vX.Y.Z | A release, once tagged. |
nginx inside the image serves the built frontend and reverse-proxies /api to
Node on loopback, both under supervisord. Only 8080 is exposed. Put TLS in
front of it and you are done — there is no second container to route between.
docker run -d --name skribe -p 8080:8080 \
-e DATABASE_URL="postgres://user:pass@host:5432/skribe" \
-e JWT_SECRET="$(openssl rand -base64 48)" \
-e APP_URL="http://localhost:8080" \
-e AI_API_KEY="sk-…" \
-v skribe-uploads:/data/uploads \
yavadmin/skribe:latest
Migrations run at start. Open http://localhost:8080 and the first person to arrive is offered a setup screen and becomes the administrator.
services:
app:
image: yavadmin/skribe:latest
restart: unless-stopped
ports:
- "8080:8080"
environment:
DATABASE_URL: postgres://skribe:skribe@postgres:5432/skribe
JWT_SECRET: change-me-openssl-rand-base64-48
APP_URL: http://localhost:8080
AI_API_KEY: sk-…
volumes:
- uploads:/data/uploads
depends_on:
postgres:
condition: service_healthy
postgres:
# pgvector, not plain postgres — semantic search will not migrate without it.
image: pgvector/pgvector:pg17
restart: unless-stopped
environment:
POSTGRES_USER: skribe
POSTGRES_PASSWORD: skribe
POSTGRES_DB: skribe
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U skribe -d skribe"]
interval: 5s
retries: 10
volumes:
pgdata:
uploads:
pgvector. The plain postgres image does not have it,
and migrations will fail against one./data/uploads, or any S3-compatible
store when S3_* is configured.STT_BASE_URL.| Variable | |
|---|---|
DATABASE_URL | Required. Postgres with pgvector. |
JWT_SECRET | Required. openssl rand -base64 48. Back it up — see below. |
APP_URL | The address the browser actually uses. Reset links, OAuth redirect URIs and invitations are built from it. |
AI_API_KEY | Your provider key. |
AI_PROVIDER | openai (default), anthropic, or local. |
ADMIN_EMAIL / ADMIN_PASSWORD | Creates the administrator at boot, skipping the setup screen. |
ALLOW_REGISTRATION | false once your account exists. Invitations still work. |
COOKIE_SECURE | true behind TLS. |
STORAGE_DRIVER | Inferred. Set S3_ENDPOINT or S3_ACCESS_KEY_ID to use an object store. |
SMTP_* | Without it, password resets and invitations are logged, not sent. |
Every option is documented inline in
.env.example.
GET /healthz returns JSON and 200. Use it as your platform's health check.
Back up JWT_SECRET with your database. It signs sessions, reset links,
OAuth state and attachment URLs. Restoring a database without it signs
everyone out and turns every image already written into a note into a broken
link.
APP_URL causes more failed deployments than anything else. If sign-in
works but "Continue with Google" fails, or reset emails point at localhost,
that is what to check first.
linux/amd64. linux/arm64 is published when the repository opts in.
Content type
Image
Digest
sha256:69ab9e74e…
Size
214.9 MB
Last updated
about 2 months ago
docker pull yavadmin/skribe