Files
Primora/README.md
T
Tomas DvorakandDevin fe28902c4a ci: image caching, Trivy scans, edge publishing, standalone installer
- docker-build job is now a per-app matrix with GHA layer cache, a
  container contents smoke check, and a non-blocking Trivy scan.
- New docker-publish job pushes edge + sha tags to GHCR on master.
- Fixed release.yml tagging ghcr.io/<Owner>/... with a mixed-case
  owner — GHCR requires lowercase, so tagged releases would have
  failed at push time.
- compose files: image: keys name local builds after the GHCR repos.
- install.sh: curl|bash installer — fetches compose files, generates
  secrets, pulls prebuilt images (docker pull + up --no-build, since
  compose pull skips buildable services), falls back to a source
  tarball build when packages aren't public.
- README: install section covering the one-liner, clone, and prebuilt
  paths plus common compose commands.

Verified locally: all three images build, full dev-overlay stack
passes scripts/smoke-e2e.sh, and install.sh produces a healthy
stack in a standalone directory.

Authored By: TDvorak <[email protected]>

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-21 13:22:25 +02:00

6.6 KiB

Primora

A self-hosted backend platform for building products — auth, Postgres-backed APIs, object storage, JSON document collections, API keys, and a full audit log behind one dashboard. Inspired by the UX of Appwrite and Supabase, with no hosted tier: your data stays on your hardware.

Browser ──▶ Nginx ──▶ Frontend (SolidJS + Vite)
                 ├──▶ Auth service (Better Auth + Hono)
                 └──▶ API (Go + Gin) ──▶ PostgreSQL
                                     ──▶ DragonflyDB (Redis-compatible cache)
                                     ──▶ Local filesystem object storage

What you get

  • Organizations & projects — top-level workspaces containing projects, members, and scoped roles (owner/admin/member org roles, admin/developer/viewer project roles).
  • Auth — email/password plus optional GitHub, Google, Discord, and Microsoft OAuth via Better Auth. JWTs minted by the auth service are verified by the Go API against JWKS.
  • Storage — S3-style buckets and objects backed by your local filesystem, with public/private visibility and downloadable URLs.
  • Collections — schema-flexible JSON documents stored in Postgres JSONB.
  • API keys — prm_<prefix>_<secret> credentials; secrets are shown once.
  • Audit log — every mutating request recorded with actor, resource, request ID, and timestamp; CSV/JSON export from the dashboard.
  • Generated client — the TypeScript client is generated from apps/backend/openapi/openapi.yaml, so the API contract is the source of truth.
  • Demo mode — a fully client-side workspace (?demo=true or VITE_DEMO_MODE=true) for trying the UI without a backend.

Install

Prerequisites: Docker with the Compose plugin.

One-liner (no clone)

curl -fsSL https://raw.githubusercontent.com/Dvorinka/Primora/master/install.sh | bash

Installs into ./primora, generates secrets, and starts the stack on port 80. Non-interactive — configure with env vars:

DOMAIN=example.com NGINX_PORT=8080 PRIMORA_DIR=/opt/primora bash install.sh

The installer pulls prebuilt images from GHCR when they are published; otherwise it downloads the source tarball and builds locally.

From a clone

git clone https://github.com/Dvorinka/Primora.git && cd Primora
./scripts/setup.sh          # creates .env, generates secrets, starts the stack

or manually:

cp .env.example .env        # fill JWT_SECRET and BETTER_AUTH_SECRET
docker compose up -d        # builds the three app images from source

To run published images instead of building:

docker pull ghcr.io/dvorinka/primora-{backend,auth,frontend}:latest
docker compose up -d --no-build

Then open http://localhost. For local email capture, add the dev overlay (-f docker-compose.dev.yml) and Mailpit is at http://localhost/mailpit/.

If ports 5432 or 6379 are already in use on your machine, set POSTGRES_PORT and DRAGONFLY_PORT in .env — only the host bindings change.

Useful commands

docker compose ps                  # service status + health
docker compose logs -f backend     # follow one service
docker compose pull && docker compose up -d   # update to latest release
docker compose down                # stop (data persists in volumes)
docker compose down -v             # stop and delete all data

Verification

Check Endpoint
Frontend http://localhost/
Backend liveness http://localhost/api/v1/health/liveness
Backend readiness http://localhost/api/v1/health/readiness
Auth health http://localhost/auth-health
OpenAPI spec http://localhost/api/v1/openapi.yaml

Local development

docker compose up -d postgres dragonfly mailpit   # infrastructure only
cd apps/backend && go run ./cmd/server            # API on :8080
npm run dev:auth                                  # auth on :3001
npm run dev:frontend                              # dashboard (Vite)

Point the frontend at the stack by adding to apps/frontend/.env.local:

VITE_API_BASE_URL=http://localhost/api/v1
VITE_AUTH_BASE_URL=http://localhost/auth

Or skip all of it: http://localhost:<vite-port>/?demo=true runs entirely in the browser.

CLI

npm run build --workspace @primora/cli   # → apps/cli/dist/cli.js
node apps/cli/dist/cli.js login          # or: npx primora login once published

Supports session sign-in (primora login) and API-key mode (primora login --api-key prm_… or PRIMORA_API_KEY). Context lives in ~/.config/primora/config.json; primora use picks org + project interactively. Commands: orgs, projects, buckets, objects (list/upload/download/rm), keys, audit list --follow. Every command accepts --json.

Desktop & phone

  • Desktop — apps/desktop is a Tauri 2 shell (Linux/Windows/macOS). First run asks for your deployment URL, then renders the same dashboard in a webview with a system-tray presence. Nothing is bundled; your server does the work. See apps/desktop/README.md.
  • Phone / PWA — the dashboard ships a web manifest and service worker. Open your deployment in a mobile browser and "Add to Home Screen"; the shell is precached, API traffic always goes to the network. Chromium desktop browsers get an in-app install banner.

Quality gate

npm run check        # backend tests + frontend typecheck + build + generated-code drift check
npm test             # workspace tests
npm run generate:sqlc    # regenerate sqlc after changing queries/schema
npm run generate:client  # regenerate the TS client after changing openapi.yaml

Repository layout

apps/
  backend/     Go + Gin API, sqlc, migrations, object storage
  auth/        Better Auth on Hono (sessions, JWT, OAuth, mail)
  frontend/    SolidJS + Tailwind dashboard
  cli/         `primora` CLI — login, context, projects, buckets, objects, keys, audit
  mcp/         `@primora/mcp` — MCP stdio server exposing `primora_*` tools
  desktop/     Tauri 2 shell — connects to a deployment URL, tray presence
packages/
  api-client/  OpenAPI-generated TypeScript client (do not hand-edit)
  shared-types/
infra/nginx/   Reverse proxy config
scripts/       setup.sh, verify-production-ready.sh

Documentation

License

MIT