Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
c440e28
chore(deps): tidy dependencies, release profile and local CI gate
SIIR3X Aug 8, 2026
f09e746
feat(config): strict configuration, atomic attempt budgets and explic…
SIIR3X Aug 8, 2026
69c30d6
fix(security): prevent second factor bypass and brute force
SIIR3X Aug 9, 2026
ac02268
fix(security): remove account enumeration oracles and bound inputs
SIIR3X Aug 10, 2026
50a2c51
fix(auth): absolute session lifetime, refresh races and email change
SIIR3X Aug 14, 2026
e1c6f60
refactor(auth): remove risk scoring and make user events durable
SIIR3X Aug 15, 2026
6f7a76b
feat(auth): client registry and hardened device authorization flow
SIIR3X Aug 15, 2026
f8b3de4
feat(auth): add authorization code flow with PKCE
SIIR3X Aug 16, 2026
b8e61b6
perf(db): align indexes with queries and batch cleanups
SIIR3X Aug 21, 2026
1f84ad2
perf(redis): single round trip token checks and O(1) rate limiter
SIIR3X Aug 22, 2026
b9ad27c
refactor: split auth and config modules, fix nginx and docker setup
SIIR3X Aug 28, 2026
d1d9c68
feat(account): audit history, 2FA overview, key rotation and localize…
SIIR3X Aug 29, 2026
b6c5933
docs: generate OpenAPI spec and rewrite documentation
SIIR3X Aug 29, 2026
7a3a12b
fix(tests): lower openapi route count sanity check
SIIR3X Aug 29, 2026
85b08b5
perf: add load testing harness and performance report
SIIR3X Aug 30, 2026
c31421b
perf(db): apply performance report recommendations
SIIR3X Aug 31, 2026
c924c3c
test: add testkit with injectable clock, mail capture and fault proxy
SIIR3X Sep 3, 2026
77beb9c
refactor(tests): split suites into integration, security and simulation
SIIR3X Sep 3, 2026
4771417
refactor(tests): port SQL suites to sqlx and check responses against …
SIIR3X Sep 4, 2026
582fbe1
test(security): enforce the OpenAPI contract, add authorization matri…
SIIR3X Sep 5, 2026
06a72ae
fix(resilience): answer 503 on dependency outages and recover the Red…
SIIR3X Sep 5, 2026
edae794
fix(security): lockout threshold, atomic reauth budget, IPv6 failure …
SIIR3X Sep 5, 2026
d7545b7
fix(security): refresh without Redis, recovery method binding and sta…
SIIR3X Sep 5, 2026
8286b5b
test: property tests for validators, cursors, buckets, keyring and lo…
SIIR3X Sep 6, 2026
a5dbd76
test: mutation testing, repository tests and coverage floors
SIIR3X Sep 6, 2026
817fac1
docs: translate the performance report and keep the repository in Eng…
SIIR3X Sep 8, 2026
4797eaa
fix(security): device session limit lock, capped session expiry, TOTP…
SIIR3X Sep 9, 2026
31a0ebf
test: config loading without environment mutation, state build errors…
SIIR3X Sep 10, 2026
fcffae8
refactor: move security decisions out of the services into tested dom…
SIIR3X Sep 12, 2026
564461f
test: account lifecycle model, timing enumeration and soak simulations
SIIR3X Sep 12, 2026
dd24699
test: raise coverage floors to the measured levels
SIIR3X Sep 12, 2026
39cdecb
docs: record the first soak run
SIIR3X Sep 12, 2026
56aeb85
docs: compare the soak run with the campaign figures
SIIR3X Sep 12, 2026
b5d6d19
fix(deploy): authenticate to NATS, require CAPTCHA and SMTP secrets a…
SIIR3X Sep 13, 2026
9df9e6f
build(release): distroless image, signed bundles built from the commi…
SIIR3X Sep 13, 2026
113b4df
feat(ops): liveness and readiness probes, bounded NATS publishing and…
SIIR3X Sep 13, 2026
addceb6
feat(ops): bounded shutdown drain, tracked background tasks and poole…
SIIR3X Sep 13, 2026
4d583cd
feat(ops): pool and error metrics, request id spans, redacted config …
SIIR3X Sep 13, 2026
c78bd0a
feat(auth): announce the next signing key and verify access tokens by…
SIIR3X Sep 13, 2026
174d6d4
feat(deploy): two API instances sized by profile, rolling updates and…
SIIR3X Sep 14, 2026
55f8401
feat(deploy): nginx failover across instances, proxy timeouts above t…
SIIR3X Sep 14, 2026
6657fb0
feat(deploy): PostgreSQL and Redis settings, restricted Redis user an…
SIIR3X Sep 14, 2026
7585028
feat(deploy): atomic encrypted backups with metrics, transactional re…
SIIR3X Sep 14, 2026
ae5a3bf
feat(deploy): monitoring host with infrastructure alerts, external pr…
SIIR3X Sep 15, 2026
21b2e9d
build: stack smoke test, sizing validation under container quotas and…
SIIR3X Sep 15, 2026
07d51eb
fix(perf): sizing run no longer stops on a CPU count without a profile
SIIR3X Sep 15, 2026
665b715
docs(deploy): runbook for NATS, SMTP and full Redis incidents, new in…
SIIR3X Sep 15, 2026
c3734b1
refactor(db): consolidate the migrations into one file per domain in …
SIIR3X Sep 15, 2026
2a0355f
docs(deploy): sizing measured under container quotas at 1 million acc…
SIIR3X Sep 15, 2026
80b2342
ci: GitHub Actions for pull requests to main, scheduled checks, back-…
SIIR3X Sep 15, 2026
03f60a0
test: stable timing simulation with production Argon2 and optimized h…
SIIR3X Sep 16, 2026
f5cc06e
feat(events): transactional outbox relayed to JetStream in order, wit…
SIIR3X Sep 16, 2026
f276446
feat(account): atomic account deletion with its user.deleted event th…
SIIR3X Sep 16, 2026
3b0aa46
feat(account): resend verification, purge of never-verified accounts …
SIIR3X Sep 16, 2026
56ba2fb
feat(privacy): forget sign-in attempts and audit addresses of deleted…
SIIR3X Sep 16, 2026
22c6ed5
feat(security): refuse breached passwords through the Pwned Passwords…
SIIR3X Sep 16, 2026
5178d29
feat(sessions): keep the current session on a password change or when…
SIIR3X Sep 16, 2026
8f76d06
feat(security): alert the owner of a sign-in from a new device
SIIR3X Sep 16, 2026
9962c00
feat(admin): admin role with second-factor gated permissions, grant-r…
SIIR3X Sep 16, 2026
e55eaf4
feat(admin): role, permission, client application and global audit ad…
SIIR3X Sep 16, 2026
7b5f119
feat(privacy): export everything stored about an account as a JSON do…
SIIR3X Sep 16, 2026
90ea627
feat(auth): opt-in sign-in links by email that keep the second factor
SIIR3X Sep 16, 2026
791f9eb
feat(account): scoped personal access tokens exchanged for short-live…
SIIR3X Sep 17, 2026
74f67c4
feat(webhooks): signed webhook deliveries recorded with each event, w…
SIIR3X Sep 17, 2026
3a43b43
feat(oauth)!: standard OAuth 2.1 authorize, token and device authoriz…
SIIR3X Sep 17, 2026
724e34c
feat(oauth): token introspection for confidential clients and token r…
SIIR3X Sep 17, 2026
7a8a4a7
feat(oauth): client credentials grant for confidential clients
SIIR3X Sep 17, 2026
d94121c
feat(oidc): OpenID Connect provider with ID tokens, UserInfo and disc…
SIIR3X Sep 17, 2026
2c93107
feat(passkeys): WebAuthn passkey registration and passwordless sign-in
SIIR3X Sep 17, 2026
4b9cf7d
feat(auth): sign-in with Google, GitHub and OpenID Connect providers …
SIIR3X Sep 17, 2026
dbfda43
feat(observability): OpenTelemetry request traces exported over OTLP/…
SIIR3X Sep 17, 2026
40e7adc
docs(deploy): self-hosted high availability guide with NATS cluster s…
SIIR3X Sep 17, 2026
94a1ab9
feat(deploy): optional read replica for lag-tolerant reads and sizing…
SIIR3X Sep 17, 2026
928bb47
feat(verifier): internal Rust crate verifying access tokens in resour…
SIIR3X Sep 17, 2026
dec6b17
feat(verifier): internal npm package verifying access tokens in Node.…
SIIR3X Sep 18, 2026
9af2df6
docs(integration): guide to flows, token verification and account eve…
SIIR3X Sep 18, 2026
ae004c8
docs(security): STRIDE threat model mapped to the control catalog
SIIR3X Sep 18, 2026
e91db84
docs(release): versioning, deprecation and support policy
SIIR3X Sep 18, 2026
987fc15
docs(security): vulnerability reporting policy
SIIR3X Sep 18, 2026
aad31ff
chore(release): auth-api 2.0.0
SIIR3X Sep 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
26 changes: 26 additions & 0 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# cargo-nextest configuration.
#
# Every test runs in its own process against its own database (cloned from a
# migrated template), so tests are safe to run fully in parallel.

[profile.default]
# Flag a test as slow after 30 s and kill it after 2 minutes: a hung test (a
# stalled Redis or NATS call) must fail loudly instead of freezing the run.
slow-timeout = { period = "30s", terminate-after = 4 }
# No retries: a flaky test is a bug to fix, not noise to hide.
retries = 0

# Simulations hold dependencies down or slow on purpose, and the long
# scenarios run for minutes.
[[profile.default.overrides]]
filter = 'binary(simulation)'
slow-timeout = { period = "60s", terminate-after = 20 }

[profile.ci]
# Report every failure in one run.
fail-fast = false
failure-output = "immediate-final"

[profile.ci.junit]
# target/nextest/ci/junit.xml
path = "junit.xml"
13 changes: 12 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# Git
.git
.gitignore
.gitleaks.toml
trivy-secret.yaml

# Rust build artifacts
target/
# The image builds with the toolchain of its pinned Rust base image.
rust-toolchain.toml

# Environment files
.env
Expand All @@ -14,6 +16,15 @@ target/
# Documentation
docs/
reports/
CHANGELOG.md

# Not part of the build: release bundles, perf and ops tooling, fuzzing
dist/
perf/
scripts/
nginx/
fuzz/
config.prod.env
README.md
LICENSE

Expand Down
16 changes: 5 additions & 11 deletions .env.dev
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ APP_ENV=development
SERVER_HOST=0.0.0.0
SERVER_PORT=3000
APP_PUBLIC_URL=http://localhost:3000
# Web application the emails link to (/verify-email, /reset-password).
# Blank: same as APP_PUBLIC_URL.
FRONTEND_URL=
TRUSTED_PROXY_CIDRS=

# Database (matches docker-compose.dev.yml)
Expand All @@ -27,9 +30,8 @@ JWT_ACCESS_EXPIRY_SECS=900
JWT_REFRESH_EXPIRY_SECS=2592000
JWT_STRICT_SESSION_BINDING=false
# Audience values stamped into the `aud` claim of access tokens (CSV).
# Each entry must match the `expected_aud` (or APP_PUBLIC_URL fallback) of a
# downstream resource server that will accept tokens minted here.
# Local stack ports: billing-api on :3001, core-api on :3002.
# Each entry names a downstream resource server that accepts tokens minted
# here; APP_PUBLIC_URL is always added. Example: two local services.
JWT_AUDIENCE=http://localhost:3001,http://localhost:3002

# Argon2id (lighter settings for dev)
Expand All @@ -56,14 +58,6 @@ LOCKOUT_THRESHOLD=100
LOCKOUT_DURATION_SECS=60
SENSITIVE_ACTION_REAUTH_SECS=600

# GeoIP (disabled in dev)
GEOIP_DB_PATH=
GEOIP_REQUIRED=false
RISK_ALERT_THRESHOLD=30
RISK_CHALLENGE_THRESHOLD=60
RISK_BLOCK_THRESHOLD=80
RISK_HISTORY_DAYS=90

# SMTP (Mailpit - matches docker-compose.dev.yml)
SMTP_HOST=mailpit
SMTP_PORT=1025
Expand Down
67 changes: 67 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Dependency updates, monthly and grouped, to keep noise and CI runs low:
# one pull request per ecosystem for minor and patch updates, one for major
# updates (a deliberate migration, but visible rather than ignored).
#
# Pull requests target staging, the draft branch: they are checked by the CI
# when staging is proposed to main. Security updates always target main.

version: 2

updates:
- package-ecosystem: cargo
directories: ["/", "/fuzz"]
target-branch: staging
open-pull-requests-limit: 2
schedule:
interval: monthly
time: "06:00"
timezone: Europe/Paris
groups:
cargo:
update-types: [minor, patch]
cargo-major:
update-types: [major]
ignore:
# sqlx 0.8's `ipnetwork` feature is tied to the 0.20 line: a second
# ipnetwork version breaks every sqlx Type/Encode/Decode bound on the
# IpNetwork columns. Drop this once sqlx supports ipnetwork 0.21+.
- dependency-name: ipnetwork
update-types: ["version-update:semver-major", "version-update:semver-minor"]

- package-ecosystem: github-actions
directory: "/"
target-branch: staging
open-pull-requests-limit: 1
schedule:
interval: monthly
time: "06:00"
timezone: Europe/Paris
groups:
actions:
patterns: ["*"]

# Base images of the Dockerfiles, pinned by digest.
- package-ecosystem: docker
directory: "/"
target-branch: staging
open-pull-requests-limit: 1
schedule:
interval: monthly
time: "06:00"
timezone: Europe/Paris
groups:
dockerfiles:
patterns: ["*"]

# Images of the compose files, pinned by digest.
- package-ecosystem: docker-compose
directories: ["/", "/deploy/monitoring"]
target-branch: staging
open-pull-requests-limit: 1
schedule:
interval: monthly
time: "06:00"
timezone: Europe/Paris
groups:
compose:
patterns: ["*"]
46 changes: 46 additions & 0 deletions .github/workflows/backmerge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Back-merge

# staging is the draft branch that pull requests to main come from. After a
# merge, main holds a merge commit that staging lacks: fast-forward staging so
# the next pull request starts from main. When draft commits were pushed to
# staging in the meantime, the fast-forward is impossible: the job leaves
# staging alone and warns, and staging is brought up to date by hand.
#
# Merge staging into main with a merge commit (not squash or rebase): staging
# then stays an ancestor of main and the fast-forward works.

on:
push:
branches: [main]

permissions:
contents: read

concurrency:
group: backmerge

jobs:
fast-forward:
name: Fast-forward staging to main
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0

- name: Fast-forward
run: |
if ! git fetch origin staging 2> /dev/null; then
echo "No staging branch: nothing to do."
exit 0
fi
if git merge-base --is-ancestor origin/staging HEAD; then
git push origin HEAD:refs/heads/staging
echo "staging fast-forwarded to $(git rev-parse --short HEAD)."
else
echo "::warning::staging has commits that are not on main; bring it up to date by hand."
fi
Loading
Loading