Skip to content

Roadmap

A snapshot of what Modgud does today and where it's heading. The page gets revised when something lands — the current state lives here, not in a changelog that ages between releases.

What ships today

Authentication

  • Password + TOTP + Email OTP + Passkey (FIDO2/WebAuthn) + Magic Link, combinable per user
  • OIDC + SAML 2.0 federated login — Microsoft Entra ID and standards-compatible OIDC/SAML IdPs — with JIT user provisioning and a JavaScript claim-mapping script. OIDC supports self-service account linking (Profile → Linked accounts); SAML linking currently resolves through the normal sign-in/JIT or trusted-email path because the cross-site ACS POST cannot carry the SameSite=Lax application cookie. Admin force-unlink and re-link after disconnect are supported for both.
  • Configurable authentication levels (password-only, secure-login with 2FA enrolment, passwordless-only) with a grace-period workflow for migrating existing users
  • Account-lockout, session tracking with device info (UAParser), per-session revoke + "log out everywhere"
  • Public self-registration with a per-app posture — sign-in-or-sign-up on first OTP, an explicit register endpoint, invite-code-gated sign-up, or switched off entirely
  • Configurable per-realm rate-limit ceilings on auth endpoints (OTP request, magic-link request, password reset, passkey ceremonies, …)

Authorization

  • RBAC inside per-app permission catalogs (<resource>:<action> format); see Permissions & gating
  • Two-tier bypass model (<resource>:admin, realm:admin) with IdP-side pre-expansion so resource servers do exact-match lookups
  • Groups with manual or script-based ("Auto-Membership") membership, nested groups with cycle detection, per-group app activation via BoundTo
  • Per-Audience resource_access emission in JWT access tokens, UserInfo and authorized introspection responses when the matching audience and claim scopes are present, with bypass pre-expansion and per-RS subset narrowing; native via the Modgud.AspNetCore.ResourceServer NuGet package

Positions and shared terminals (Technical Preview)

  • Feature-flagged position principals for shared workplace terminals: administrators assign people to positions, terminals are bound to one or more positions, and the person currently staffing a terminal receives the position's effective authorization without turning the device into a user account
  • Flexible activation proofs (personal passkey, password, email OTP, or an individually revocable position token) and immutable terminal-binding profiles (DPoP key, client secret, or explicitly unbound), constrained by realm security floors and position policy
  • Complete enrollment and staffing lifecycle: RFC 8628 admin approval, refresh-time policy revalidation, targeted invalidation cascades, bounded step-up tokens, V1-to-multi-position control-token compatibility, and an admin UI for positions, terminal slots, activation tokens, and Staffing OAuth clients
  • Ships off by default. Operators enable it with AppSettings__Features__PositionTerminals=true; see Feature flags, the admin guide, and the consumer contract

OAuth 2.0 / OpenID Connect (OpenIddict 7)

  • Authorization Code + PKCE, Client Credentials, Refresh Token, Device Code (RFC 8628)
  • Reference tokens by default (instantly revocable); per-client JWT switch when stateless verification matters
  • Per-realm issuer + discovery document — https://<realm>/.well-known/openid-configuration
  • Dynamic Client Registration (RFC 7591) with triple opt-in (realm master + per-API + per-scope), audience-target containment, audit events
  • Service accounts as first-class principals — client_credentials- only OAuth clients linked 1:N to a ServiceAccount so audit logs and the Group → Role → Permission chain work the same for machines and humans
  • Client ID Metadata Documents — an https URL as client_id for dynamic, unregistered clients, SSRF-hardened and opt-in per realm
  • Native cookieless grants (email-OTP, magic-link, passkey) for mobile/native apps that can't hold a browser session, plus per-client WebAuthn RP-ID for passkeys

Multi-tenancy

  • Database-per-realm via Marten MasterTableTenancy; domain-based routing — no path-prefix acrobatics, no tenant_id columns
  • Control-Plane / tenant-realm split — cross-realm admin lives on a separate App catalog, gated by middleware + endpoint filter + database-level isolation
  • Per-realm DataProtection keys persisted in the tenant DB so cookies and anti-forgery tokens survive restarts and never cross realms
  • First-admin bootstrap via recovery CLI or a control-plane POST /api/admin/realms with InitialAdmin payload
  • Applications as a soft per-tenant facet — per-app origin, branding, login posture, and self-registration settings, sharing the realm's user pool with a single sub
  • Declarative realm provisioning — import/export a realm as a manifest, apply it in place, and optionally prune anything the manifest no longer lists

Operations

  • Two instances against one PostgreSQL, and image updates without downtime: replace one container after the other behind a sticky reverse proxy with active readiness checks. Wolverine coordinates projections and the outbox, Quartz runs clustered, live updates cross nodes over the SignalARRR Postgres backplane, every node resolves login providers and sessions from the database — no second stateful service. Each release says whether it is safe to roll. First increment; see Running two instances
  • OpenTelemetry: metrics + traces, Prometheus scrape endpoint (Bearer-gated), custom IdP meter (login attempts, token issuance, realm operations), in-app live activity feed
  • Quartz-scheduled background jobs with admin surface — schedules, history, manual trigger
  • Operator inbox for notifications + per-kind retention policy
  • Per-realm and per-Application branding — logo, favicon, primary color, product name — consistently applied across every fixed auth surface
  • Application-specific login methods and ordered OIDC/SAML provider allow-lists, enforced at protocol entry points
  • Branded DE/EN transactional email with HTML + plain-text alternatives, safe variable escaping, Application-aware context and Mailpit-compatible development delivery
  • Asset library — BYTEA-backed store with SVG sanitisation and a 2 MB cap, ETag-served via /api/assets/{id} (public)
  • Page-builder editor/runtime (Beta, feature-flagged) for tenants that need to replace complete page structure; ordinary branding does not require it
  • Recovery CLI for break-glass admin operations (bootstrap-admin, set-email, magic-link, reset-2fa, list, rebuild-projections, realm-add-domain)
  • Auth Log — Serilog-sink-backed audit trail with a configurable per-realm visibility window (90 days by default) on the read surface — history itself isn't deleted when the window rolls

Compliance + safety

  • GDPR self-service — Article-20 data export; account deletion with a grace/cancel window (self-service — sign in to cancel before the deadline) and an admin recycle bin (deactivate + restore until the retention window elapses), both converging on an irreversible permanent erase: Marten data-masking + ArchiveStream (incl. external-identity links) while preserving audit-chain integrity. Windows are per-realm.
  • Profile change-request flow with admin approval + email-ownership double-opt-in (configurable per realm)
  • Hardening track record — dependency audit, CodeQL, SAST in CI; JsEval fuzzing for the membership-script attack surface; PII-masking convention for logs

What's coming next

In rough priority order. None of these have a hard date — the page updates when something ships.

High

  • Multi-instance operation, next steps — two instances against one PostgreSQL are supported with no second stateful service (Wolverine-coordinated projections and outbox, clustered Quartz, SignalARRR backplane with a live-update cluster subject on the master database; see Running two instances). Still open: persisting the live observability feeds so the admin view is not per node, and a CI job that runs the previous release next to the current build to enforce the "rolling update: safe" rule automatically.

Medium

  • Enterprise SSO — LDAP + Kerberos — SAML 2.0 federation ships today (see SAML federation); the LoginProvider aggregate also reserves Ldap and Kerberos types for the handlers that come next.
  • NodaTime migration — move scheduled-event fields (deactivation dates, time-boxed group memberships, credential rotation, GDPR sweeps, password-expiry policies, maintenance windows) from UTC timestamps to a proper local-date-plus-timezone representation, so an admin-intended local time survives a DST rule change correctly.
  • Login alerts + manual IP blacklist — surface suspicious-login events to operators with an explicit allow/deny action, as a NAT-safer alternative to auto-rate-limiting that risks locking out legitimate users behind a shared IP.

Lower

HSM/KMS for signing keys, per-realm provisioning quotas, bulk user import, SCIM provisioning, step-up authentication, risk-based authentication, more locales beyond DE/EN, compliance certifications (SOC 2, ISO 27001).

Deliberate non-goals

  • A backup/restore product. Modgud does not ship its own backup scheduler, snapshot format, or restore tooling. Backing up Modgud is standard PostgreSQL operations, and database-per-realm makes per-tenant backups straightforward — each realm is one database, so pg_dump per database (or whatever backup tooling the operator already runs for Postgres) is all that's needed. Restore is a standard Postgres restore of the realm database(s) plus the master/system databases. See Backing up realms for the operational detail. This is a scope decision, not a gap — the product's job is the clean per-realm database layout that makes operator-owned backup tooling straightforward to apply, not a reimplementation of Postgres backup. A possible future convenience — tooling to restore a realm's data into a new realm slug — may show up later as a low-priority nicety, not because backup itself is missing.

Where to follow along

There's no separate design-notes tree to browse — progress shows up as it ships, tracked on GitHub Issues and Releases.

Have an opinion on priority, or a use case that would benefit from something on the lower list moving up? Open a Discussion — it's the fastest way to influence what's next.

Released under the Apache-2.0 License.