46 releases (26 breaking)
Uses new Rust 2024
| new 0.29.11 | May 9, 2026 |
|---|---|
| 0.28.0 | May 3, 2026 |
#528 in Authentication
1MB
22K
SLoC
sui-id
A self-hosted, single-binary OpenID Connect provider written in Rust. Quiet, careful, and small enough to read end to end.
Overview
sui-id is an Identity-as-a-Service you run yourself. It speaks OpenID Connect on the front end, stores its data in a single encrypted SQLite file, and ships as one statically linked binary. There is no separate database service, no embedded JavaScript runtime, and no ambient cloud dependency.
The name "sui" comes from Latin sui generis — "of its own kind." sui-id is not aiming to replace large IDaaS products. It is built for the case where you want an OIDC provider that one person can hold in their head and one operator can keep healthy on a single VM.
Why
Running an IDaaS in production usually means accepting one of two trade-offs: delegate identity to a SaaS vendor (lose control, gain auditability), or stand up Keycloak / Authelia / Authentik (gain control, gain a pile of moving parts). sui-id picks a different point in the design space:
- Single binary, single SQLite file. No JVM, no separate token database,
no message bus.
cpis a backup. - Encryption that doesn't depend on filesystem trust. Sensitive columns
are sealed with XChaCha20-Poly1305 using a master key kept outside the
database file. A stolen
.sqliteis not a compromised one. - A protocol surface narrow enough to audit. Authorization Code with mandatory PKCE, EdDSA-signed tokens, opaque rotating refresh tokens. No implicit flow, no hybrid flow, no RS256 by default.
- A UI that wants to be quiet. Server-rendered HTML, no client-side JS bundle, dark-mode aware.
If you want SAML, LDAP federation, dynamic client registration over the internet, or twenty IdP integrations out of the box: sui-id is not for you, and that's a feature.
Quick start
# Install the binary from crates.io
cargo install sui-id
# Generate a starter config
sui-id --print-sample-config > sui-id.toml
# Edit issuer / paths if needed, then start
sui-id --config sui-id.toml
If you'd rather build from source:
git clone https://github.com/nabbisen/sui-id && cd sui-id
cargo build --release
./target/release/sui-id --print-sample-config > sui-id.toml
./target/release/sui-id --config sui-id.toml
On first run sui-id will:
- Create a fresh 32-byte master key at the path in
[storage].key_filewith permissions0600. Back this file up. Without it, the encrypted columns of the SQLite file are unreadable. - Print a one-time setup token to stderr.
- Wait for you to open
/setupin a browser and complete the wizard.
After setup, point your relying party at:
| Endpoint | Path |
|---|---|
| Discovery | /.well-known/openid-configuration |
| JWKS | /.well-known/jwks.json |
| Authorization | /oauth2/authorize |
| Token | /oauth2/token |
| Userinfo | /oauth2/userinfo |
| Admin UI | /admin |
Features
- OpenID Connect Core 1.0 (Authorization Code + PKCE only)
- OAuth 2.0 Refresh Token grant with token rotation on each use
- EdDSA / Ed25519 token signing, advertised through JWKS
- Argon2id password hashing
- Field-level encryption of refresh tokens and signing-key material
- Append-only audit log including authentication outcomes
- Per-IP rate limiting on login, token, and setup endpoints
- Background garbage collection of expired authorization codes, sessions, and refresh tokens
/healthzendpoint that does not leak system state- Setup wizard with one-time token, no default credentials
- TOML configuration; master key resolved from env or file
- Single-process, single-binary, single-file deployment
- Built on Rust 1.91 with
unsafe_code = "forbid"enforced workspace-wide
Design notes
- Storage: SQLite via
rusqlitewith the bundled feature; one connection, WAL mode. The schema lives incrates/sui-id-store/src/migrations/. - Crypto: XChaCha20-Poly1305 for column encryption; Ed25519 for JWT signing; Argon2id for passwords. Implementations are pulled from the RustCrypto ecosystem.
- HTTP: Axum 0.8 over Tokio. The router is one file:
crates/sui-id/src/router.rs. - UI: Leptos 0.8 in SSR-only mode. No WASM is shipped; pages are rendered
server-side and HTML POSTs handle state changes. JavaScript is reserved for
the single
confirm()prompt on destructive actions. - Observability:
tracing+tracing-subscriber. Choosefmtorjsonoutput via config.
Project layout
crates/
├── sui-id-shared DTOs, typed ids, public error type
├── sui-id-store SQLite, migrations, column encryption, repositories
├── sui-id-core Domain logic: passwords, JWT, OIDC, setup, sessions
├── sui-id-web Leptos SSR pages (login, setup, admin panel)
└── sui-id Axum router, config loader, master-key resolution,
embedded static assets, the `sui-id` binary
docs/ Operator and integrator documentation
Documentation
docs/deployment.md— chronological, opinionated walkthrough from a fresh Linux server to a hardened production install. Start here for a first-time deployment.docs/operators.md— reference for configuration fields, the master key, GC, the audit log schema, and routine operational tasks.docs/integrators.md— pointing an application at a sui-id instance: discovery, registration, the OIDC flow, and the shape of the tokens.docs/threat-model.md— what sui-id defends against, what it does not, and what assumptions the operator must uphold for the design to work. crates.io. Not relevant to end users.
Dependencies
~75–100MB
~2M SLoC