Quickstart
Two paths: local dev (run from source against Postgres in Docker) and self-hosted (Docker compose with the published image). Both get you to "a verified Apple sandbox transaction" in roughly 5–10 minutes once you have Apple credentials in hand.
Local dev — from source
Prerequisites
- mise — toolchain manager. Pins Deno, flyctl, Node, and pnpm to repeatable versions.
- Docker — for local Postgres (the dev container is just Postgres; the app runs natively under
deno).
Setup
git clone https://github.com/nossdev/attesto.git
cd attesto
mise install # installs Deno 2.7.12 + flyctl + Node 20 + pnpm 9
cp .mise.local.toml.example .mise.local.tomlEdit .mise.local.toml and set at minimum:
[env]
DATABASE_URL = "postgres://attesto:attesto@localhost:5432/attesto"
ATTESTO_ENCRYPTION_KEY = "<run: openssl rand -base64 32>"Generate the encryption key:
openssl rand -base64 32Paste the output into .mise.local.toml. Don't lose this — losing it makes every encrypted tenant credential unrecoverable.
Boot it
mise run db:up # starts Postgres in Docker (port 5432)
mise run db:migrate # applies all migrations
mise run dev # starts Attesto on :8080Smoke-test in a second terminal:
curl http://localhost:8080/health
# → {"status":"ok"}
curl http://localhost:8080/ready
# → {"status":"ok","checks":{"db":"ok","encryption":"ok"}}Both 200 OK means the binary boots, the DB is reachable, and the encryption key decodes correctly.
Create your first tenant
mise run cli -- tenant:create --name "My First App"Output:
{ "id": "tenant_01HXY...", "name": "My First App", "createdAt": "2026-04-25T..." }Copy the id — you'll need it.
Mint an API key
mise run cli -- key:create tenant_01HXY... --env test --name "dev machine"Output:
{
"id": "key_01HXY...",
"tenantId": "tenant_01HXY...",
"keyPrefix": "a8Fz3Q1c",
"name": "dev machine",
"rawKey": "attesto_test_8xYz...",
"warning": "Save the rawKey — it cannot be recovered after this line."
}The rawKey is shown exactly once. Save it into your .mise.local.toml or a password manager:
ATTESTO_KEY = "attesto_test_8xYz..."Test an authenticated endpoint
Once you have Apple or Google credentials configured (see Apple setup / Google setup), you can verify a transaction:
curl -X POST http://localhost:8080/v1/apple/verify \
-H "Authorization: Bearer $ATTESTO_KEY" \
-H "Content-Type: application/json" \
-d '{"transactionId":"2000000123456789"}'That's the local dev loop. From here go to Apple setup or Google setup to wire up real verification.
Self-hosted — Docker compose
Best for trying out Attesto without installing Deno on your machine, or as the starting point for a production self-host.
Prerequisites
- Docker + Docker Compose (most modern Docker installs include compose v2)
Setup
git clone https://github.com/nossdev/attesto.git
cd attesto
cp .env.example .envEdit .env:
ATTESTO_ENCRYPTION_KEY=<run: openssl rand -base64 32>The bundled docker-compose.yml defines the app + Postgres with healthchecks and migrate-on-start. Bring it up:
docker compose up -d
docker compose ps
# → app and db both "healthy"Smoke-test:
curl http://localhost:8080/health
curl http://localhost:8080/readyRun admin operations against the running container
The same attesto binary handles serve, migrate, and admin subcommands. For self-hosted setups, exec into the container:
docker compose exec attesto attesto tenant:create --name "My First App"
docker compose exec attesto attesto key:create tenant_01HXY... --env testFor production self-hosting, see Deployment.
Pull the image directly
If you don't want the bundled compose stack — for example you have your own Postgres — just pull the image:
docker pull ghcr.io/nossdev/attesto:latestRun it directly with your own DATABASE_URL and ATTESTO_ENCRYPTION_KEY:
docker run --rm -e DATABASE_URL=postgres://… ghcr.io/nossdev/attesto:latest attesto migrate
docker run -d \
-e DATABASE_URL=postgres://… \
-e ATTESTO_ENCRYPTION_KEY=… \
-p 8080:8080 \
ghcr.io/nossdev/attesto:latestThe image is multi-arch (amd64 + arm64), runs as non-root with tini as PID 1, and exposes the attesto binary as the entrypoint.
Next steps
- Apple setup — wire up your first Apple verification
- Google setup — wire up Google Play
- Webhooks — receive Apple S2S / Google RTDN events
- Architecture — what's actually happening under the hood