Purpose of this file: Single source of truth for any AI agent (Copilot, Cursor, Cline, Claude Code, etc.) working on the Cosmian Authentication Server codebase. It explains project structure, build commands, CI workflows, coding conventions, and troubleshooting steps.
Cosmian Authentication Server is a high-performance authentication and session management server written in Rust. It supports multiple database backends (SQLite, PostgreSQL, MySQL), TOTP two-factor authentication, JWT-based sessions, and a Redis session store.
# ── Build ────────────────────────────────────────────────────────────────
cargo build # default features (OpenSSL, database)
cargo build --features rustls # use rustls instead of OpenSSL
# ── Test ─────────────────────────────────────────────────────────────────
cargo test --workspace --lib # run all library tests
cargo test -p auth_verifier # single crate
# ── Lint ─────────────────────────────────────────────────────────────────
cargo clippy --workspace --all-targets -- -D warnings
cargo fmt --all -- --check
# ── Run locally ──────────────────────────────────────────────────────────
cargo run --bin auth_verifier -- auth_verifier.toml
# ── Smoke-test (expect 200 or 404, not 500) ─────────────────────────────
curl -s http://localhost:8443/healthAlways install and never bypass pre-commit hooks:
pip install pre-commit conventional-pre-commit
pre-commit install
pre-commit install --install-hooks -t commit-msgNever use git commit --no-verify or SKIP=... to bypass hooks. Fix the
underlying issues instead.
client/ auth_client — authentication client library
server/ auth_verifier — server binary + lib
src/
main.rs — binary entry point
lib.rs — library root
database/ — database trait and backends (SQLite, PostgreSQL, MySQL)
middleware/ — auth/JWT/session middleware
response/ — HTTP response types
server/ — server startup and config
session/ — session management
tests/ — integration tests
tls/ — TLS helpers
nix/ Nix build expressions and expected vendor hashes
auth-verifier.nix — Nix derivation for auth_verifier binary
docker.nix — Docker image derivation
expected-hashes/ — expected sha256 hashes for reproducible builds
signing-keys/ — GPG public keys for package verification
.github/
scripts/ — CI and packaging scripts
common.sh — shared bash helpers
nix.sh — unified entrypoint for CI commands
release/ — version extraction and hash update scripts
package/ — DEB / RPM / DMG packaging scripts
test/ — test scripts (sqlite, psql, docker)
workflows/ — GitHub Actions workflows
reusable_scripts/ — git submodule: shared scripts with Cosmian/reusable_scripts
default.nix — top-level Nix derivation (pins nixpkgs, builds auth-verifier)
shell.nix — Nix development shell
Cargo.toml — workspace manifest
| Feature | Default | Effect |
|---|---|---|
openssl |
on | Use OpenSSL (vendored) for TLS; required for most deploys |
rustls |
off | Use rustls instead of OpenSSL |
database |
on | Compile all database backends (SQLite, PostgreSQL, MySQL) |
no_jwt_validation |
off | Skip JWT expiry/issuer checks — dev/test only |
| Intent | File(s) |
|---|---|
| Server startup | server/src/main.rs, server/src/lib.rs |
| Server config struct | server/src/server/ |
| HTTP routes & handlers | server/src/server/ |
| Auth middleware (JWT, session) | server/src/middleware/ |
| Database trait & backends | server/src/database/ |
| Session management | server/src/session/ |
| TOTP support | server/src/totp.rs |
| OpenAPI schema | server/documentation/openapi.yaml |
| Nix derivation | nix/auth-verifier.nix |
| Nix top-level | default.nix |
| CI/packaging entrypoint | .github/scripts/nix.sh |
| Packaging scripts (DEB/RPM/DMG) | .github/scripts/package/ |
| Test scripts | .github/scripts/test/ |
server/documentation/openapi.yaml is the authoritative API contract. Every
change that touches a route, request body, response body, or authentication
requirement must be reflected in all three layers at the same time:
server/src/server/endpoints/ ←→ client/src/ ←→ server/documentation/openapi.yaml
| Layer | What to check |
|---|---|
Server routes (server/src/server/endpoints/*.rs) |
HTTP method, URL path, path parameters, query parameters, request body type, response status codes |
Server app (server/src/server/auth_verifier.rs) |
Scope prefix + middleware stack (which routes require cookieAuth) |
Client DTOs (client/src/dto/, client/src/models/) |
Struct field names and types that are serialized/deserialized over the wire |
Client methods (client/src/client/auth_client.rs) |
URL format strings, HTTP methods, request/response types |
OpenAPI schema (server/documentation/openapi.yaml) |
Paths, parameter names, schema component field names, security requirements, examples |
-
Route change (add, rename, remove a path or HTTP method):
- Update the actix-web
#[get/post/put/delete("...")]macro in the endpoint file. - Update
auth_verifier.rsscope registration if the path prefix changes. - Update the matching URL format string in
auth_client.rs. - Add/rename/delete the corresponding path entry in
openapi.yaml.
- Update the actix-web
-
Request or response body change (add, rename, or remove a field):
- Update the Rust struct in
client/src/dto/orclient/src/models/. - Update the matching
components/schemas/entry inopenapi.yaml. - Update any inline examples in
openapi.yamlthat use the changed field.
- Update the Rust struct in
-
Authentication change (a route gains or loses an auth requirement):
- Update the middleware wrap chain in
auth_verifier.rs. - Update the
security:list on the corresponding path inopenapi.yaml.
- Update the middleware wrap chain in
-
New endpoint:
- Add the handler in the appropriate
*_endpoints.rsfile. - Register it in
auth_verifier.rs. - Add the client method in
auth_client.rs. - Add the full path entry (summary, operationId, parameters, requestBody,
responses, security, example) in
openapi.yaml.
- Add the handler in the appropriate
- Path parameter names in route macros (
/{realm_id}/) must match thename:of the corresponding$ref: '#/components/parameters/...'entry. - Rust struct field names are serialized as-is (no
#[serde(rename)]unless explicitly needed). OpenAPIpropertieskeys must match exactly. passwordinUserPassis always aVec<u8>/ integer array on the wire — returned as[]on reads; never echoed back.
# 1. Build compiles cleanly
cargo build --workspace
# 2. All tests pass
cargo test --workspace --lib
# 3. No clippy warnings
cargo clippy --workspace --all-targets -- -D warnings
# 4. Manually cross-check openapi.yaml against the endpoint files:
grep -r '#\[get\|#\[post\|#\[put\|#\[delete' server/src/server/endpoints/
# Every route macro must have a matching path in openapi.yaml.nix/auth-verifier.nix builds the auth_verifier binary targeting glibc 2.34
(Rocky Linux 9 compatibility) on Linux. It uses:
- Pinned nixpkgs
8b27c1239e5c421a2bbc2c65d52e4a6fbf2ff296(matches KMS repo) - nixpkgs 22.05 (glibc 2.34) as the Linux stdenv
- Rust 1.86.0 via rust-overlay
- Vendored OpenSSL (compiled during cargo build, no external OpenSSL needed)
cmakeandperlas native build inputs (required byaws-lc-sysand openssl crate)
No FIPS / non-FIPS variants — the auth server has a single build variant.
# Build static binary
nix-build -A auth-verifier-static
# Build dynamic binary
nix-build -A auth-verifier-dynamic
# Build Docker image (Linux only)
nix-build -A docker-image# Full packaging via nix.sh:
bash .github/scripts/nix.sh --link static package # DEB + RPM on Linux, DMG on macOS
bash .github/scripts/nix.sh --link static package deb # DEB only
bash .github/scripts/nix.sh --link static package rpm # RPM only
bash .github/scripts/nix.sh --link static package dmg # DMG only (macOS)
# Docker (Linux only):
bash .github/scripts/nix.sh docker --load| File | Purpose |
|---|---|
server.vendor.static.sha256 |
Cargo vendor hash for static builds |
server.vendor.dynamic.sha256 |
Cargo vendor hash for dynamic builds |
auth-verifier.<link>.<arch>.<os>.sha256 |
Expected binary hash for determinism check |
When Cargo.lock changes, the vendor hashes become stale. Regenerate:
# Put fake hash, run build, read "got:" error, paste correct hash
echo "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" > nix/expected-hashes/server.vendor.static.sha256
nix-build -A auth-verifier-static 2>&1 | grep "got:"All CI runs go through .github/scripts/nix.sh:
bash .github/scripts/nix.sh [--link static|dynamic] COMMAND [args]| Command | Description |
|---|---|
test |
Run all tests in nix-shell |
test sqlite |
SQLite backend tests only |
test psql |
PostgreSQL backend tests (requires server) |
package |
Build all packages for this platform |
package deb |
Build Debian package |
package rpm |
Build RPM package |
package dmg |
Build macOS DMG (macOS only) |
docker [opts] |
Build Docker image tarball (Linux only) |
update-hashes |
Regenerate expected binary hashes |
| Workflow | Purpose |
|---|---|
main.yml |
Push/PR trigger; calls main_base.yml |
main_base.yml |
clippy, cargo-deny, cargo-test, packaging |
packaging.yml |
Multi-platform packaging (Linux/ARM/macOS) + Docker |
packaging-tests.yml |
Install packages in Docker containers and verify |
For PostgreSQL tests:
| Variable | Value |
|---|---|
POSTGRES_HOST |
127.0.0.1 |
POSTGRES_PORT |
5432 |
Always use GH_PAGER=cat to prevent interactive pager. The repository is
Cosmian/authentication.
GH_PAGER=cat gh issue view <number> --repo Cosmian/authentication
GH_PAGER=cat gh pr view <number> --repo Cosmian/authentication
GH_PAGER=cat gh pr checks <number> --repo Cosmian/authentication
GH_PAGER=cat gh run view <run-id> --repo Cosmian/authentication --log-failedEvery agent-driven change must be recorded in the single per-branch changelog file.
- File name:
CHANGELOG/<branch-name>.md— one file per branch, named after the current git branch with any/replaced by_(e.g. branchspire→CHANGELOG/spire.md, branchfix/user-to-admin→CHANGELOG/fix_user-to-admin.md). Never create a new file per change (no<short_slug>.mdfiles). - Append, don't proliferate: add each new entry as a bullet under the appropriate category heading in the existing branch file. Create the file only if it does not yet exist.
- Format: one or more category headings (
## Features,## Bug Fixes,## Refactor,## CI,## Docs,## Tests) with bullet points beneath. Keep the file clear and compact: merge related bullets, avoid duplication, and group all changes of the same category together. - Each bullet must be a single complete sentence summarising what changed and why, sufficient for a human to understand without reading the diff.
- Do not add a changelog entry for pure formatting/linting-only commits.
CHANGELOG/<branch_name>.md
After every edit to .rs files, run:
cargo fmt --all
cargo clippy --workspace --all-targets -- -D warningsFix all clippy warnings before considering the task complete.
- Function length: keep functions under 100 lines; extract helpers for longer ones.
- Imports: Rust
usestatements go at the top of each file, never inline. - Error handling: never ignore or skip errors in tests or builds — investigate and fix.
- Commit scope: minimal, focused changes. Do not refactor surrounding code alongside a bug fix.
| Symptom | Cause | Fix |
|---|---|---|
aws-lc-sys / cmake build failure |
Missing cmake/go in build env | Add cmake and go to nativeBuildInputs / shell.nix |
Stale Nix vendor hashes after Cargo.lock change |
Expected hash is outdated | Regenerate with fake-hash trick (see §6) |
tokenExpired / JWT validation error |
Feature no_jwt_validation disabled in prod |
Check configuration; check token TTL |
gh command hangs |
Interactive pager opened | Use GH_PAGER=cat gh ... |
| Rocky Linux GLIBC compatibility error | Binary compiled against glibc > 2.34 | Ensure pkgs234 (glibc 2.34) stdenv is used in Nix |
SECURITY.md at the repo root is a hand-maintained security policy and
vulnerability-disclosure ledger. It is edited directly (unlike the generated
root CHANGELOG.md) and has three sections that must stay mutually consistent:
the table of contents, the per-advisory entries, and the bottom summary table.
- IDs use the repo-scoped form
COSMIAN-AUTH-<YYYY>-<NNN>(three-digit, zero-padded), distinct from the KMS repo'sCOSMIAN-<YYYY>-<NNN>. - To allocate a new ID, read the highest existing
COSMIAN-AUTH-<YYYY>-NNNfor the current year and increment by one.
- Add an entry only when the fix repairs a vulnerability that already shipped
in a tagged release, so the
Affectedrange points at real version tags (e.g.from 0.1.0 before 0.3.0). - A bug that only ever lived on a branch and was fixed before any release gets no entry — no users were exposed.
- The entry lands only after the fix is merged. Never document an unfixed vulnerability here.
- Add the
#### COSMIAN-AUTH-<YYYY>-NNN — <title>heading with its field table (Severity / Published / Affected / Fixed in / Found by / References) followed by Summary / Impact / Mitigation prose. - Add the matching anchor link under the table of contents.
- Add a matching row to the Summary Table.
All three updates are required for every advisory; there is no automated check, so consistency is the author's responsibility.
SECURITY.mdis never touched by changelog generation; edit it by hand.Securitybullets inCHANGELOG.mdandCHANGELOG/<branch>.mdshould cite the relevantCOSMIAN-AUTH-<YYYY>-NNNIDs so the two stay cross-referenced.
A generation prompt/skill and regression tests annotated with the advisory IDs are planned as future work; today the lifecycle is enforced by these rules and reviewer diligence.