• Rust 46.9%
  • Vue 26.5%
  • TypeScript 24.8%
  • CSS 0.7%
  • JavaScript 0.6%
  • Other 0.4%
Find a file
Lucas Jahier 3c6cc07379
Some checks failed
CI / Style (push) Has been cancelled
CI / Test (push) Has been cancelled
chore: update totp app link
2026-03-18 16:37:34 +01:00
.config chore: update ci 2025-12-22 03:53:13 +01:00
.github/workflows chore: update ci 2025-12-22 03:53:13 +01:00
app chore: update totp app link 2026-03-18 16:37:34 +01:00
crates feat: add db source age public keys for file encryption + support multiple keys 2026-03-18 15:19:05 +01:00
migrations feat: add db source age public keys for file encryption + support multiple keys 2026-03-18 15:19:05 +01:00
scripts chore: cleanup + add docker compose for dev 2025-12-22 03:53:13 +01:00
src feat: add db source age public keys for file encryption + support multiple keys 2026-03-18 15:19:05 +01:00
.dockerignore feat: admin + cleanup 2026-02-08 04:43:57 +00:00
.env.sample feat: listen api on unix socket 2026-03-06 17:26:32 +01:00
.gitignore chore: cleanup + add docker compose for dev 2025-12-22 03:53:13 +01:00
Cargo.lock feat: add db source age public keys for file encryption + support multiple keys 2026-03-18 15:19:05 +01:00
Cargo.toml feat: add db source age public keys for file encryption + support multiple keys 2026-03-18 15:19:05 +01:00
compose.dev.yml chore: cleanup + add docker compose for dev 2025-12-22 03:53:13 +01:00
config.cfg feat: add assets_dir path to the config file 2025-08-03 15:45:21 +02:00
Dockerfile Docker build env 2025-08-03 15:39:33 +02:00
Dockerfile.rust.build chore: remove musl + add documenso public url env var added to private documenso 2026-03-06 17:15:35 +01:00
Dockerfile.web.build feat: admin + cleanup 2026-02-08 04:43:57 +00:00
Makefile chore: remove musl + add documenso public url env var added to private documenso 2026-03-06 17:15:35 +01:00
README.md feat: add db source age public keys for file encryption + support multiple keys 2026-03-18 15:19:05 +01:00
rust-toolchain.toml feat: admin + cleanup 2026-02-08 04:43:57 +00:00
rustfmt.toml feat(api): convert to multi-crate workspace 2025-12-22 03:53:13 +01:00

Overview

Agir P.U.R.R is a fullstack Rust + Vue 3 application:

  • Backend Axum / SQLx (Postgres) served from /api
  • Frontend Vue 3 + Vite - static build served from / by the same binary

Prerequisites

Tool Version (tested) Notes
Rust stable 1.88 rustup toolchain install stable
pnpm 10.x deterministic JS installs ([pnpm.io][1])
Node 24 LTS for the Vue build
Postgres = 15 running locally on localhost:5432

Local development

# 1. Clone
git clone ssh://git@git.asso-purr.eu.org:2222/ljahier/agir-purr.git
cd agir-purr

# 2. Build Rust deps (creates target/)
cargo build         # dev profile

# 3. JS deps + Vite dev server
# create a `.env` file in `app/` directory with `VITE_API_URL=http://localhost:3000/api` value
cd app
pnpm install        # installs dependencies
pnpm dev            # http://localhost:5173

# 4. Init DB (from repo/migrations)
cd ..
cargo run -- upgrade   # wraps `sqlx migrate run` under the hood

# 5. Configure encryption key (required)
cargo run -- keys add --public-key age1... --label local-dev

# 6. Run backend
cargo run -- server start --user   # user API/UI runtime
# or
cargo run -- server start --admin  # admin API/UI runtime

Edit: configuration is provided via environment variables (see Production config below).


Production build (Docker)

Builds happen inside Docker and export artifacts to dist/ only.

make prod x86_64-unknown-linux-gnu

Outputs:

  • dist/bin/agird
  • dist/admin.zip
  • dist/users.zip

For ARM:

make prod aarch64-unknown-linux-gnu

Note: make prod relies on Docker BuildKit for --output. Docker Desktop enables this by default.

Production run (systemd + Caddy)

  1. Unzip the static apps and serve them with Caddy:
unzip -q dist/admin.zip -d /var/www/agir-admin
unzip -q dist/users.zip -d /var/www/agir-users
  1. Run database migrations:
dist/bin/agird upgrade
  1. Add at least one age encryption key (required before server startup):
dist/bin/agird keys add --public-key age1... --label primary
  1. Run two daemons using the same binary but different env vars:
dist/bin/agird server start --admin
dist/bin/agird server start --user

Use systemd EnvironmentFile= for shared config and Environment= to override per daemon (e.g. UNIX_SOCKS_PATH and CORS_ORIGIN).

Production config (env vars)

Required:

  • DATABASE_URL
  • LISTEN_ADDR
  • UPLOADS_DIR
  • CORS_ORIGIN
  • TOTP_ISSUER
  • TOTP_ENCRYPTION_KEY (64 hex chars)
  • SESSION_TTL_SECONDS
  • DOCUMENSO_API_URL
  • DOCUMENSO_APP_URL
  • DOCUMENSO_API_KEY
  • DOCUMENSO_TEMPLATE_ID
  • DOCUMENSO_WEBHOOK_SECRET

Optional:

  • UNIX_SOCKS_PATH (if set, binds a Unix domain socket instead of LISTEN_ADDR)
  • COOKIE_SECURE (defaults to true)
  • CSP (if set, sent as Content-Security-Policy)
  • RUST_LOG (standard Rust log filter, e.g. info,agird=debug)

The binary reads .env at startup (via dotenvy) if present, but in production it is typical to set env vars via systemd.

Encryption keys

Age public keys are now stored in Postgres (encryption_keys table), not in env vars. The server refuses to start when no key is configured. New mandat files are encrypted for all configured public keys.

# list configured keys
agird keys list

# add a key
agird keys add --public-key age1... --label primary

# remove by UUID or label (cannot remove the last remaining key)
agird keys remove <id-or-label>

# re-encrypt existing .age files using current DB public keys
# --identity points to an age identity file containing private key(s)
agird keys regenerate --identity /path/to/identities.txt

Generate TypeScript SDK

cargo run --features openapi -- export-openapi -o app/openapi-admin.json --admin
cargo run --features openapi -- export-openapi -o app/openapi-admin.json
cd app && npm run generate-client

Output in sdk/agir-user/src/ with typed API client.