Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, this project adheres to Semantic Versioning, commits follow Conventional Commits, and this changelog is generated by Structured Changelog.
Unreleased¶
v0.17.0 - 2026-10-18¶
Highlights¶
- Relying-party applications manage managed principals through SystemAuth: a
/managed-principalsAPI served bysystemauthsvc, called with the manager's access token, and thempclientGo client - Nobody becomes a manager without agreeing: managers other than the creator join by invitation or claim
- The contract's identity, policy and audit operations refuse requests until the application is federated
Upgrade Guide¶
- Standalone applications that serve the contract API: set
Config.AllowUnauthenticatedStandalonein development and tests, or federate the application; identity, policy and audit operations otherwise answer 403 - Code calling
managedprincipal.Service.Createwith managers other than the actor, orAddManager: invite those managers or issue a claim, or build the service withWithDirectManagerAddsfor administrator tooling - Managers in roles registered with
WithRolescan no longer issue login tokens; issue them as a guardian or educator - To let a relying party manage managed principals, enable
managed_principalsfor the deployment and its client and addsystemforge:managed_principalsto the client's scopes
Added¶
- SystemAuth built by
systemauthsvcserves the managed-principal API at/managed-principalswhen managed principals are enabled, for relying parties calling with a manager's access token grantedsystemforge:managed_principals;Options.AuditEmitter,ManagedPrincipalRatesandManagedPrincipalOptions, andService.ManagedPrincipals()(a76d041) managedprincipal.NewHandlerrelying-party mode (WithRequestClient): the client is the access token's, creation names only the caller, unknown and unmanaged principals answer 403;WithCodeRateLimitfor code routes;GET /(Service.ListManagedBy) andPOST /{id}/login-tokens(af30b5b)managedprincipal/mpclient, a typed client for the managed-principal API with errors wrapped in*APIError(eaca813)systemauth.Server.BearerAccessandBearerAuthenticator: bearer access that carries the token's client, scopes and grant, adapted torequestprincipal(c0c4e8d)
Changed¶
- BREAKING: The contract's identity, policy and audit operations answer 403 until the application is federated;
Config.AllowUnauthenticatedStandalonerestores the open behavior for development and tests (9883be5) - BREAKING:
managedprincipal.Service.Createaccepts only the actor as manager andAddManageris refused withErrConsentRequired, so managers join by invitation or claim;WithDirectManagerAddskeeps direct adds for administrator tooling (53a2655) - Issuing a login token requires
manage_credentials(bf1c347) - The managed-principal handler answers 403 for an unknown or unmanaged principal, as for one the caller does not manage (
0124078)
Documentation¶
- Managed principals guide covers the API on SystemAuth, its configuration and scope, and the client
- INIT-SYSTEMFORGE-012 specification for managed principals in relying-party applications (
a18e9f1) - v0.17.0 release notes; relying-party, embedding and README updates for the managed-principal API
Tests¶
- The connections test checks the error
RunJobsreturns when its context ends (078231b)
v0.16.0 - 2026-10-14¶
Highlights¶
- Connections can be reconnected in place, track what depends on them with bindings, and be shared with a principal through per-connection role grants
- The connection library is renamed from connectionforge to connectforge; imports, tables (
connectforge_*) and the key purpose (connectforge.credential) change, which is breaking for deployments that enabled connections in v0.15.0
Upgrade Guide¶
- Update imports from connectionforge to connectforge
- Deployments that enabled connections in v0.15.0 must re-create connections or rename the tables and re-seal credentials; migrate does not copy rows or re-seal
- Run
systemauth migrateto create theconnectforge_*tables
Added¶
POST .../connections/{id}/reconnect,GET,PUTandDELETE .../connections/{id}/bindings, andGET,PUTandDELETE .../connections/{id}/grants/{principal}(5a4bf33)systemauthsvcregisters thesystemforge_connectionresource type so connection grants are role grants read by the role-based provider (8f501c3)
Changed¶
- BREAKING: Depend on connectforge instead of connectionforge; tables are now
connectforge_*and credentials are sealed under theconnectforge.credentialpurpose (7eb410b)
Dependencies¶
- 1 dependency update
Documentation¶
- Connections guide covers reconnecting, bindings and grants; ADR-017 updated for the rename and the grant vocabulary (
0a5c80b)
Tests¶
- Cover reconnect, bindings and grants over the API (
26d6b64)
v0.15.0 - 2026-10-13¶
Highlights¶
- Key management:
keyswires keyforge as the platform's one key library, with envelope encryption, rotation without re-encrypting, the root key read through omnivault, and key events sent to the audit trail (ADR-013) - Connections to external services:
connectionscomposes connectionforge so organizations connect accounts at services such as a CDN or analytics product, with credentials sealed under managed keys and applications receiving an authenticated HTTP client, never the secret (ADR-017) - A connections API at
/api/v1/organizations/{org}/connections, enabled by the newconnectionsconfiguration section and protected bysystemforge:connections:readand:writescopes - Invite-only sign-up: a gate on social login that admits only verified emails holding an invite, a consent-recording waitlist for everyone else, and
systemauth inviteandsystemauth waitlistcommands - App catalog: a host publishes the applications it runs, and the browser session lists it so each shell can show the products a person holds a role in, for a product switcher
- Embedded SystemAuth emits the
orgsclaim with per-application roles, limited to the deployment's default entitlements - Fixed
rls.Helper.SetTenantandSetUser, which failed on every PostgreSQL connection becauseSET LOCALdoes not accept bind parameters - No breaking changes; run
systemauth migratefor the new tables
Upgrade Guide¶
- Run
systemauth migrate(orsystemauthsvc.Service.Migrate) to createsf_signup_invitesandsf_signup_waitlist. Existing tables are unchanged and no SQL migration file is needed - To enable connections, add the
connectionssection, create the root key (openssl rand -base64 32) and store it in the vault, and back upkeys_dirwith the database.systemauth migratethen creates theconnectionforge_*tables and enables row-level security on PostgreSQL
Added¶
keyspackage:Open,SealandOpenover keyforge's single-host setup, withVaultSourcereading the root key from an omnivault vault, and an audit sink that records lifecycle events without key material (1f3095e)connectionspackage: connectionforge's SQL store on the platform database with row-level security keyed onowner_id, credentials sealed bykeys, deployment secrets read from omnivault, authorization throughauthz, and audit events to the platform trail (5975b1e)connections.RolePermissionsgrants organization owners and admins view, use and manage on connections, and members view (dfcd4ed)connections.Handler: the organization-scoped connections HTTP API (providers, list, connect, get, rename, test, disconnect) that never returns a credential and reports a refused view as not found (dfcd4ed)connections.MigrateSchemacreates the connection tables and enables row-level security without opening the keys, sosystemauth migrateneeds no root key (dfcd4ed)systemauth.Server.BearerPrincipalandWriteBearerErrorauthenticate API requests by bearer access token and scope with RFC 6750 responses (ccc1d03)connectionssection of the SystemAuth configuration (keys_dir,vault,root_key,allow_private_egress), thesystemauthsvc.Options.Providersregistry,Service.ConnectionsandService.RunJobs(7b51e06)systemauth serveruns the key lifecycle jobs while serving (dfb3597)systemauth invite add|list|revokeand the--invite-onlyand--signup-denied-urlflags, withSignupInvite(sf_signup_invites), thesignupgatestore and gate, andsystemauth.SignupGateon social login (3471a81)- For host binaries:
systemauthsvc.Options.InviteOnly,SignupDeniedURLandWaitlistPage,Service.SignupInvitesandService.SignupWaitlist, and thesignupgate/signupcmdpackage with theinviteandwaitlistcommand groups to mount in a host's own CLI (dc2af08) - Sign-up waitlist:
/signup/waitlistrecords an address with the consent text shown and its version, withSignupWaitlistEntry(sf_signup_waitlist),signupgate.Waitlistandsystemauth waitlist list [--csv]andremove(72ac85b) appcatalogpackage:Appand an ordered, validatedCatalog, withStaticandRemotesources, the last cached and kept through an outage (538133c)systemauth.WithAppCatalogserves the catalog at/.well-known/sf-apps;relyingparty.BFFConfig.Appsadds the catalog to/bff/sessionasapps, which a shell filters by the membership'sapplications;systemauthsvc.Options.Appspublishes a host's catalog and warns about entries with no registered vocabulary (31cc7a9)systemauthsvc.Options.VocabulariesandDefaultEntitlementsinstall an Ent-backedorgsclaim source with application roles fromapprole(cffe439)
Fixed¶
rls.Helper.SetTenantandSetUseruseset_config(name, value, true), which has the transaction scope ofSET LOCALand takes the value as a parameter; the previousSET LOCAL ... = $1was a PostgreSQL syntax error (6149cc1)
Dependencies¶
- 2 dependency updates
Documentation¶
- Guides for connections to external services and for key management, and sections for them in the deployment guide and README (
ee8b6d7) - Deployment guide sections for invite-only sign-up with the waitlist, and for the app catalog (
2190863) - ADR-017 on external connections, ADR-016 on no static API keys and ADR-015 on identifier policy, slug namespaces and portability (
cac014f) - ADR-013 makes keyforge the platform's key library, with the key subsystem, rotation policy, hybrid encryption, FIPS 140-3 and post-quantum positions, rotation and recovery drills, and the crypto specification; ADR-011 and ADR-012 add purge, provisional accounts, dormancy and offload (
5460d4f)
Tests¶
rlsis checked against a real PostgreSQL server, and the connections store is checked as an ordinary database role, which row-level security applies to (77b8d12)- The connections API is exercised end to end against a fake provider, including that no response carries a token (
b9bb2db)
v0.14.0 - 2026-10-11¶
Highlights¶
- Managed principals: humans without an email, such as young learners, created and controlled by guardian and educator managers, who sign them in with QR-friendly login tokens; opt-in per deployment and per client (ADR-005)
- Principal is the sole identity root: the legacy User model and its dependent tables are removed, so password login, social login, federation sync, and SCIM all resolve a person to the same principal (ADR-004)
- Resource servers can authenticate AI agents with AAuth through a protocol-neutral request principal and authenticator chain shared with bearer, API-key, and BFF-session credentials
- Fixed a panic on every ascending SCIM list sort, including the default order
- Security: the marketplace, organization, and invite APIs act for the authenticated caller and check organization permissions, instead of trusting IDs in the request; invite tokens are stored hashed
- The simple and SpiceDB authorization providers grant the same organization and manager permissions, held to the base schema by a parity test and a shared conformance suite
- Organizations without a plan: a first social login creates the person's personal organization, slugs are reserved, renamed with a cooling-off period and aliases, a personal organization converts to a team, and what an organization may use comes from its marketplace subscriptions and licenses (ADR-006)
- Application roles per member, entitled through the marketplace, carried in the
orgsclaim and on invites, with relying parties reading memberships from claims by default (ADR-007) - Real-time invalidation of access: access tokens default to 10 minutes and are capped at 15, every access-reducing change is recorded, and relying parties refuse tokens issued before it, with polling, replay and back-channel delivery (ADR-010)
- Resource-scoped collaborators: the
collaboratororganization role, resource grants with a standard viewer, contributor and maintainer ladder in both authorization modes, and invites that target one resource (ADR-008) - The authorization mode is configuration: the
authzkey selectsrbacorrebac, andauthz/mode.Newbuilds the provider, syncer and grant store (ADR-008) - Management is derived from manager relationships; a manager can release a principal, a self-managed human can be invited to be managed or decline, and managing operations are authorized and served over HTTP (ADR-009)
- Editor and viewer are no longer organization roles, and the organization
plancolumn is removed; run migrations 0004 to 0009 on existing PostgreSQL databases
Breaking¶
- Removed Ent schemas and packages user, membership, oauthaccount, refreshtoken, apikey, serviceaccount, and serviceaccountkeypair, with the Organization memberships, api_keys, and service_accounts edges and OAuthToken.service_account_id (
05d78ae) - systemauth.Storage user methods are replaced by CreateHuman, GetHuman, GetHumanByEmail, GetHumanByExternalIdentity, LinkExternalIdentity, UpdateHuman, and DeleteHuman on one HumanPrincipal type; systemauth.User, LoginPrincipal, ErrUserNotFound, and ErrUserExists are removed in favor of HumanPrincipal, ErrPrincipalNotFound, and ErrPrincipalExists (
d054dc7) - OAuthApp.owner references Principal; statically configured clients are owned by a service principal at the fixed system ID instead of a synthetic user (
12f2494) - OAuth tokens and authorization codes drop user_id and the user edge in favor of the subject column; OAuthConsent.user_id becomes principal_id (
16f7f9f) - SCIM users are human principals and group membership uses PrincipalMembership; users provisioned before this release are not visible through SCIM (
6a12bae) - An existing PostgreSQL development database must run migrations/0002_drop_legacy_user_tables.sql before starting this version; fresh databases need no migration (
97282d7) - The default BFF session cookie is sf_session instead of the Core-era cf_session; browsers holding the old cookie sign in again. Applications that set CookieConfig.Name are unaffected (
94868b5) - organization.Service ListMembers and ListMemberships exclude inactive memberships unless IncludeInactive is passed, and take variadic ListOption arguments (
3f80884) - mixin.UserBase and mixin.MembershipBase are removed (
8c95e08) - Human.email is optional: a human without one is a managed principal with the identifier human:
. HumanPrincipal.Email, principal.Human.Email, and contract identity types treat it as optional; existing creation paths still require an email ( e89af69) - Marketplace checkout buys as the authenticated caller for an organization the caller may buy for, and answers 501 until marketplace.WithPurchaseAuthorizer or WithAuthorizer is configured; purchaser_id is optional, deprecated, and must equal the caller (
7cf758e) - Marketplace listing and license operations answer 501 until marketplace.WithAuthorizer is configured and require an authenticated caller; only published listings are readable anonymously; owner_id and assigned_by are optional, deprecated, and must equal the caller (
bbac538) - Organization API endpoints require an authenticated caller and, except creating an organization and checking a slug, answer 501 until APIConfig.Authorizer is set (
08bd206) - Invite API endpoints require an authenticated caller and organization endpoints answer 501 until APIConfig.Authorizer is set; accepting and declining require the invite to be addressed to the caller's email; inviter_principal_id, principal_id, and the email query are optional, deprecated, and must match the caller (
236c0e9) - Invite tokens are stored hashed and invite.Invite no longer has a Token field; the token is only in InviteResult.InviteURL. Existing PostgreSQL databases run migrations/0003_hash_invite_tokens.sql when deploying (
7ccc80e) - spicedb.BaseSchema is now authz.BaseSpiceDBSchema: members hold contribute rather than edit on an organization, and editors are recognized (
8762bc6) - authz.RelationshipSyncer gains AddPrincipalManager and RemovePrincipalManager; credential.Service gains IssueLoginToken and VerifyLoginToken; managedprincipal.Gate implementers also provide LoginTokenPolicy (
8acce83) - identity/token reports typed errors (ErrInvalidAccessToken, ErrInvalidRefreshToken, ErrRefreshTokenReused, ErrDPoPMismatch, ErrScopeNotGranted) (
2a3c8cd) - marketplace.LicenseService gains GetByStripeSubscriptionID; Stripe webhook endpoints should also subscribe to checkout.session.async_payment_succeeded (
fa2a57d) - delegation errors are typed and an agent without a delegation is denied; signup's SignupOrGetExisting requires SignupInput.EmailVerified to return an existing account, and SignupWithInvite requires the invited email (
284806d) - systemauth.WithUserIDHeader is removed; use WithTrustedUserIDHeader with a RequestGate (TrustedProxyNetworks or TrustAllRequests), social login, or your own SessionProvider. DefaultSessionProvider authenticates nobody by default (
645c151) - identity/oauth's authorization endpoint refuses requests until WithUserAuthenticator or WithTrustedUserIDHeader is set, and third-party clients are refused until WithConsent is set (
ef7ea3d) - session/ratelimit: IPKey and the Limiter default no longer read X-Forwarded-For or X-Real-IP (use ForwardedIPKey or ForwardedClientIP behind proxies), the middleware answers 503 on storage errors unless WithFailOpen is given, and MemoryStorage returns ErrTooManyKeys when full (
3019061) - session/bff: TrustProxy and TrustCloudflare do nothing without TrustedProxies or CloudflareIPRanges; session/middleware no longer reads forwarding headers for API key usage addresses unless an IPExtractor is given (
a253f24) - scim: a provider without WithAuthorizationHook answers 403 to every request; DefaultAuthorizationHook is deprecated for AllowAllAuthorizationHook (
0e3dd33) - systemauth: NewFederationEndpoints registers the sync and lookup routes only with WithFederationAuthenticator; introspection callers need a confidential client or the introspect scope (
d57bf0f) - session/jwt and contract: tokens without exp are refused, and jwt refuses another issuer when Config.Issuer is set; relyingparty.BearerMiddleware requires an allowed Origin and JSON body for cookie-authenticated POST, PUT, and DELETE (
7061c67) - credential.ValidateClientSecret returns ErrInvalidClientCredentials for every rejection; multiapp.AppAwareDB.ForSchema returns an error (
30bd958) - identity/apikey: Get, Revoke, and Delete take the owning principal and report other keys as not found; a service with no AllowedScopes grants no scopes unless AllowAnyScope is set; RevokeInOrganization added for organization administrators (
0a3cf11) - rls: Helper.SetTenant and SetUser return ErrNotInTransaction for sql.DB and sql.Conn; Middleware.SetRLSContext no longer touches the database and Middleware.WithTx scopes a transaction (
ba68c75) - The organization
plancolumn andorganization.Planconstants are removed from the schema, service inputs, list filter, API and SCIM group provisioning; organization responses carry anentitlementslist instead. Existing PostgreSQL databases run migrations/0004_drop_organization_plan.sql (5172a12) editorandviewerare no longer organization roles: the roles are owner, admin, member and collaborator, every membership path refuses any other value, and the base schema gains thecollaboratorrelation andlistpermission. migrations/0008_drop_editor_viewer_roles.sql maps storededitortomemberandviewertocollaborator, including pending invites (18b3a32)- Every path that creates or changes a membership validates the role against owner, admin, member and collaborator and answers ErrInvalidRole otherwise (
24edc0b) - The marketplace SpiceDB fragment no longer redefines
principalandorganization; it attaches to the base organization through amarketplace_orgfacet carrying use, billing and purchase. Compose it with MergeSchema, which places the base schema first (3774596) - Entitlement checks fail closed: an approle service with no entitlement source entitles no organization to any application. Configure marketplace.EntitlementSource through marketplace.ApproleOptions, or use approle.EntitleAll in development and tests (
2d11968) - Relying parties read memberships from SystemAuth claims by default (ClaimsMemberships); LocalMemberships is no longer picked up implicitly from the principal store and is for development and tests only (
5cd6dd9) - The default access token lifetime drops from 15 to 10 minutes, and a configured lifetime above 15 minutes is refused at load; per-client lifetimes are capped at 15 minutes (
d20202b)
Upgrade Guide¶
- Run the migrations against an existing PostgreSQL database, in order: 0004_drop_organization_plan, 0005_subscriptions_per_listing, 0006_invite_app_roles, 0007_oauth_app_backchannel_logout_uri, 0008_drop_editor_viewer_roles and 0009_invite_resource_target. Each is idempotent. Fresh databases need none of them. Then run
systemauth migratefor the new tables sf_app_roles, sf_slug_reservations, sf_access_changes, sf_resource_grants and sf_manager_invites - Replace uses of the organization plan with entitlements: remove
Planfrom organization inputs and filters, and readentitlementsfrom organization responses. Supply APIConfig.Entitlements with marketplace.EntitlementSource; carry a paid tier over as a subscription or license - Replace
editorandviewerin your own role tables, vocabularies and invites withmemberandcollaborator(or resource roles); stored memberships are mapped by migration 0008, which records an access change for each active membership it changes - Configure entitlements for application roles: build the approle service with marketplace.ApproleOptions(marketplace.EntitlementSource(...)), and declare applications free to everyone with marketplace.WithDefaultEntitlements. Without them no organization is entitled to anything, and marketplace.WarnIfNoEntitlements logs a startup warning
- Relying parties that depended on LocalMemberships must pass it explicitly. The default reads the
orgsclaim; use ClaimsOrRemoteMemberships to fall back to /oauth/userinfo/memberships when the claim is truncated or absent - Pass a revocation cache to BearerMiddleware and NewBFF to refuse tokens issued before an access change; feed it with accesschange.ToSink in one process, the poller across replicas, or back-channel delivery across deployments. Review any configured access_token_lifetime against the 15 minute cap
- Build the authorization provider from configuration with
authz/mode.New(ctx, cfg.Authz, mode.Deps{...})and pass its Syncer to the identity services. Theauthzkey defaults torbac. Theauthz/simpleandauthz/spicedbforwarding packages remain deprecated and will be removed in the next major release; importauthz/rbacandauthz/rebac/spicedb - When switching to
rebac, write existing memberships, application roles and resource grants into the graph through the syncer before serving requests, and write the composed schema (embedded instances always receive it; remote ones only withwrite_schema)
Security¶
- Marketplace checkout took the purchaser from the request body, so anyone could start a purchase in another principal's name for any organization (
7cf758e) - Marketplace listing, license, and seat endpoints acted on any IDs a request named; they now check the caller's permissions on the owning organization (
bbac538) - The organization API listed every organization and let any caller read, change, or delete organizations, change members, and transfer ownership; it now authorizes each operation (
08bd206) - Invite management needed no permission, and anyone holding an invite link could accept it as any principal; invites are now managed with invite_member and accepted only by the addressee (
236c0e9) - Invite tokens were stored in plain text, so a leaked table or backup could accept invites; they are stored as SHA-256 hashes (
7ccc80e) - SpiceDB principal and organization cleanup ignored failures and missed editor and manager relations, leaving access behind while reporting success (
22f7b28) - Refresh-token rotation was not atomic and its revocation error was discarded, so a refresh token could be used more than once; refreshing near expiry also reset a session to seven days, extending it indefinitely. Rotation is now atomic, a family keeps its first expiry, and reusing a rotated refresh token revokes the family (
2a3c8cd) - Stripe checkout granted licenses for sessions completed with an unpaid delayed payment, a malformed seat count produced an unlimited seat-based license, and cancelled subscriptions kept their licenses (
fa2a57d) - Deactivating a principal left its access tokens valid and let its refresh tokens keep issuing new ones for up to the family's absolute lifetime; SystemAuth and identity/token now refuse an inactive principal's tokens wherever they are used or refreshed, and revoke the family (
ebe913d) - Revoking an agent's delegation gave it unrestricted access, constraints that did not overlap allowed everything, and delegation expiry and action limits were never stored; delegation now fails closed and enforces every constraint (
284806d) - UpdateHuman granted or revoked platform administration without syncing it, so a revoked administrator stayed one in SpiceDB (
5e002b3) - SignupWithInvite let anyone holding an invite link join under any email, and SignupOrGetExisting returned an existing account for an unverified email (
e6d07df) - SystemAuth's default session provider and identity/oauth's authorization endpoint took the user from an X-User-ID request header, so anyone who could reach the endpoint could obtain an authorization code, and tokens, for any user; header identity is now an explicit, gated opt-in and the default authenticates nobody (
645c151) - identity/oauth granted every requested scope to every client without consent; non-first-party clients now need a consent check (
ef7ea3d) - SCIM listing and search never asked the authorization hook, so any caller with a token could dump every user and group, and the provider's default hook allowed everything; listings are authorized and the default denies (
0e3dd33) - The federation identity sync endpoint trusted any well-formed request and acted on its body, letting an unauthenticated caller link to and then deactivate or rewrite any account by email; it and the lookup endpoint now require a FederationAuthenticator and are not registered without one (
d57bf0f) - Rate limits keyed on client-supplied X-Forwarded-For and X-Real-IP, so a client could evade a limit by rotating the header or poison a victim's; they key on the direct peer, and forwarded headers are believed only from configured trusted proxies, here and in the BFF client IP handling and the API key middleware (
3019061) - The in-memory rate-limit store grew without bound from attacker-chosen keys, the middleware failed open on storage errors, and the login token endpoint keyed on raw client_id values; the store is bounded, the middleware fails closed, and only registered clients get a bucket (
3019061) - Token introspection accepted any valid access token or any client's Basic credentials, public clients included, as the caller's credential; it now requires a confidential client or a token with the introspect scope (
5e6dba0) - The default storage accepted every client-assertion JWT ID as new, so a captured private_key_jwt assertion could be replayed until it expired (
2ae4f56) - BFF cookies lost Secure and HttpOnly when a CookieConfig omitted them; both are now unconditional unless AllowInsecure is set for development (
d1d897c) - Cookie-authenticated writes through relyingparty.BearerMiddleware skipped the same-origin check, allowing cross-site requests (
ebf1095) - JWT validation accepted tokens with no expiry and ignored the issuer; the contract middleware did the same and skipped the audience check without an app ID (
7061c67) - Client secret validation revealed whether a client ID existed by error message and timing, and lockout counted differently spelled identifiers separately, restarting the count (
30bd958) - multiapp's SchemaDB left a schema's search_path on pooled connections, which could carry one application's schema to unrelated code, and ForSchema interpolated unvalidated names into SQL (
012e2db) - The BFF proxy forwarded the browser's session cookie upstream when a ProxyConfig was built by hand; Cookie, Authorization, and DPoP headers are now always stripped (
3c54fc2) - multiapp trusted the X-App-ID header, so with a shared signing key a token for one app authenticated requests for another; MiddlewareConfig.AppAudiences binds tokens to the routed app (
73e0d19) - API keys could be read, revoked, or deleted by ID alone, requested any scope including the wildcard when no allow-list was set, and ignored MaxKeysPerUser (
0a3cf11) - Lockout let a burst of parallel guesses exceed MaxAttempts; Lockout.Begin reserves the attempt before the credential is verified (
43c3818) - Concurrent BFF refreshes spent one refresh token several times, which token reuse detection treats as theft; refresh is now serialized per session (
a77d8df)
Added¶
- identity/requestprincipal: a protocol-neutral RequestPrincipal, an Authenticator interface, and middleware that tries authenticators in order and challenges with every protocol when none recognizes the request (
780ef1a) - relyingparty.NewRequestAuthenticator adapts bearer authentication (SystemAuth access tokens, API keys, BFF sessions) to the request authenticator chain (
d86c3f0) - identity/requestprincipal/aauthrs authenticates AI agents at resource servers with AAuth: message-signature and token verification, an issuer allowlist checked before key discovery, per-route scopes, and AAuth-Requirement challenges (
afd69bb) - migrations/0002 creates the system service principal, reassigns OAuth apps owned by legacy users, and drops the legacy tables and columns on existing PostgreSQL databases (
97282d7) - A repository-wide test fails on references to the retired legacy identity model in code or current documentation (
2b45328) - identity/clientassertion records used client-assertion JWT IDs in sf_client_assertion_jtis for replay protection (
bd8af95) - store.Config.TrustProvisionedEmail, off by default, marks emails written through SCIM as verified so social login can link to provisioned users (
ce3ea97) - identity/pgschema applies PostgreSQL-only schema objects; the migrate step adds a unique index on lower(email) in sf_humans (
03c314a) - The members endpoint accepts includeInactive to list deactivated memberships (
3f80884) - identity/managedprincipal creates managed principals with their managers in one transaction, maintains manager relationships with a last-manager guard, and audits every action (
7f4fda8) - ManagerRelationship (sf_manager_relationships) and ManagerClaim (sf_manager_claims) Ent schemas (
93a7ecf) - Deployment managed_principals settings and the per-client managed_principals flag, both off by default, checked through Server.ManagedPrincipalsAllowed (
12373d7) - login_token credentials: issued by a manager, hashed, verified in constant time, single-use option, with lifetimes from the request, the client's login_token_ttl, or the deployment default (30 days) under a ceiling (400 days); single-use tokens last 20 minutes (
1f1c11b) - POST /login-token exchanges a login token for a SystemAuth login session, with identical responses for every failure and per-address and per-client rate limits (WithLoginTokenRateLimit) (
f839fd4) - ID token and userinfo claim managed: true for managed principals, and relying parties read it into VerifiedIdentity.Managed (
f839fd4) - Managers list and revoke a managed principal's login tokens, login sessions, and token families, individually or all at once, through managedprincipal.Service or NewHandler (
4acc46a) - Guardian claims: a manager issues a single-use code that makes a human with an email a guardian; AttachVerifiedEmail turns a managed principal into an ordinary human (
faf4bc3) - Base schema relations guardian and educator on principal with manage_account, manage_credentials, view_profile, and manage_managers; authz.ManagerRolePermissions and simple.WithManagerRoleGetter (
8acce83) - authz.OrgRolePermissions and the PermOrg* actions name the base organization permissions both providers evaluate (
fdfe9eb) - providertest.RunOrgPermissionTests and RunManagerRelationTests hold providers to the base schema's grants (
e4279a8) - marketplace.PurchaserChecker and stripe.WithPurchaserChecker refuse checkout to managed principals (
3abd5fb) - marketplace.WithAuthorizer, WithPurchaseAuthorizer, OrgRolePurchaseAuthorizer, and WithCallerResolver configure how the marketplace API authorizes callers (
7cf758e) - organization.MembershipRoleGetter backs the simple provider with active memberships; APIConfig.Authorizer and Caller configure the organization and invite APIs (
08bd206) - requestprincipal.PrincipalUUID resolves the caller's SystemForge principal ID (
57c531b) - spicedb.Client.DeleteRelationships deletes relationships by filter (
22f7b28) - migrations/0003 converts stored invite tokens to hashes (
7ccc80e) - CI runs the PostgreSQL tests against a database service and the SpiceDB provider tests with the integration tag; SF_TEST_PG_REQUIRED makes a missing database fail rather than skip (
a5af409) - systemauth.PrincipalStatus lets storage report whether a token's principal is active; Ent and memory storage implement it (
ebe913d) - Agents store allowed actions, allowed scopes, and a delegation expiry (
404b72c) - systemauth.RequestGate, TrustAllRequests, and TrustedProxyNetworks; oauth.WithUserAuthenticator, WithTrustedUserIDHeader, WithLoginURL, and WithConsent (
ef7ea3d) - jwt.ValidateAccessTokenForAudience and middleware.HTTPAuthForAudience; ratelimit.PeerIP, ForwardedClientIP, ForwardedIPKey, WithMaxKeys, WithFailOpen; systemauth.WithLoginTokenClientIP, FederationAuthenticator, SharedSecretAuthenticator, and AuthorizeIntrospectionCaller; scim.DenyAllAuthorizationHook (
3019061) - session/jwt: TokenOptions.Audience addresses one token to its own audience (
9b46bda) - multiapp: MiddlewareConfig.AppAudiences binds tokens to the app a request is routed to (
73e0d19) - identity/security: Lockout.Begin and Attempt reserve a login attempt before verification (
43c3818) - A first GitHub or Google login creates the person's personal organization and owner membership in the same transaction as the principal, and an existing principal without one gets it on its next login; the slug is the GitHub username or the email local part, with a numeric suffix on collision (ADR-006). signup.SlugForLogin, signup.CreatePersonalOrgInTx and signup.EnsurePersonalOrgInTx expose the derivation and the in-transaction creation (
1d6f996) - Reserved slugs: organization.ReservedSlugs lists names no organization may take, WithReservedSlugs adds product names, and signup avoids them; a managed principal's slug is a manager-entered value or a generated adjective-noun-NN, never its email (signup.SlugForManaged) (
6c456ab) - Personal organization rules: a personal organization has one member, its owner, and refuses members, invitations, SCIM membership and ownership transfer with ErrPersonalOrganization unless the role is collaborator (
8988ccf) - Slug rename with a cooling-off period (default 7 days, WithSlugCoolingOff), reservations in sf_slug_reservations, and aliases through ResolveSlug so old links keep working (
f8c679a) - organization.ConvertToTeam turns a personal organization into a team in one transaction and creates a new personal organization under the handle (
1569de1) - Entitlements derived from the marketplace: organization responses carry an
entitlementslist built from active subscriptions, valid licenses and default entitlements, and subscriptions may be per listing (migration 0005) (c43fedf) - Entitled applications: a listing may name an application, and marketplace.ApproleOptions maps subscriptions, licenses and marketplace.WithDefaultEntitlements to applications with seat limits from seat-based licenses (
2d11968) - Application roles: the AppRole schema (sf_app_roles) and the approle service to assign, change and remove a role per principal, organization and application, with endpoints under /organizations/{slug}/members/{principal_id}/app-roles (
9e8d94a) - Application roles reach the authorization graph through the optional authz.AppRoleSyncer (
b530c81) - The
orgsclaim in ID tokens, access tokens and userinfo, and a paginated GET /oauth/userinfo/memberships (6b52b99) - relyingparty membership sources: ClaimsMemberships, RemoteMemberships, ClaimsOrRemoteMemberships and ProvisioningMemberships (
5cd6dd9) - Invitations carry application roles to grant on acceptance, validated against entitlements and seats (migration 0006) (
1b178fd) - sf_access_changes and the accesschange package record every access-reducing change in the transaction that makes it and announce it to a sink (
2c3082e) - Access changes are recorded for member removal and deactivation, organization and application role changes, ownership transfer, principal deactivation and session or token revocation (
f92470f) - relyingparty.RevocationCache, and BearerMiddleware and the BFF refuse tokens issued before an access change, after a clock-skew margin (
975b3a5) - Access-change poller, replay after a restart, suspect-start mode and the GET /access-changes endpoint with the access_changes:read scope (
4c0d2ce) - Back-channel delivery of access changes as OpenID Connect Back-Channel Logout tokens to clients that register backchannel_logout_uri (migration 0007), and relyingparty.BackchannelHandler to receive them (
986a1cf) - Access-change reasons form an open, validated registry (
529a999) - The resource-role ladder (viewer, contributor, maintainer) and the ResourceGranter contract (
d95eaca) - Resource grants stored in sf_resource_grants and mirrored to the graph; the role-based provider consults and manages them (
8187a9a) - The relationship-based provider writes grants as relationships and lists resources by lookup, and vocabulary fragments validate collaborator-able definitions (
68a498e) - Resource-targeted invites: an invite can name a resource type, id and role, and accepting it creates the collaborator membership and the grant together (migration 0009) (
8b399cc) - The
authzconfiguration key withrbacandrebacmodes, ModeConfig and SpiceDBConfig (537714b) - authz/mode.New builds the configured provider, relationship syncer and grant store from a ModeConfig, with Close (
dac2d76) - Management is derived from active manager relationships rather than from the absence of an email; a managed identity may carry an email (
eea12c3) - Release a principal from management, invite a self-managed human to be managed, and decline a manager invitation (
6471f2b) - A manager invitation can be declined by the invitee (
f5bef03) - Authorized managed-principal operations, deletion with erasure and an HTTP API for managing operations (
1b65fa3)
Changed¶
authzproviders are named for their mode: role-basedauthz/rbac(formerlyauthz/simple) and relationship-basedauthz/rebac/spicedb(formerlyauthz/spicedb), so another engine can sit beside SpiceDB under the same mode (f1f3c0a)- Federation sync records the federation identity as an ExternalIdentity; creating an identity for an existing email links it so later updates and deletes resolve to the same principal (
d054dc7) - Human email uniqueness is enforced case-insensitively on create and update in SystemAuth and SCIM, matching the case-insensitive lookups (
d054dc7) - SCIM userName is not case-exact (RFC 7643): lookups, uniqueness, and eq and co filters ignore case (
6a12bae) - SCIM memberships honor the active flag, and deleting a user who belongs to a group removes the memberships instead of failing (
6a12bae) - Creating an organization through the API makes the caller its owner, and every organization with an owner gives it an owner membership (
faedc5a) - Listing organizations returns the caller's organizations, or all of them for platform administrators (
08bd206) - SCIM no longer lists, reads, changes, deletes, or groups managed principals (
18431bb) - Signup requires a bare email address (
a76a721) - Unused stale Argon2id secret checkers are removed from the identity packages (
b85abc4)
Deprecated¶
authz/simpleandauthz/spicedbforward toauthz/rbacandauthz/rebac/spicedbas deprecated packages for one release; importers should move to the new paths (f1f3c0a)- mixin.HumanMixin, mixin.OAuthAccountMixin, and mixin.RefreshTokenMixin: applications should authenticate through SystemAuth and link local records by sf_principal_id (
80dcf1b) - spicedb.BaseSchema: use authz.BaseSpiceDBSchema (
8762bc6) - identity/security: Lockout.CheckAndRecord in favor of Begin (
43c3818) - rls: EntHook.SetContextFromContext, ContextInjector, ConnWithRLS, NewConnWithRLS, and GetConnWithRLS, which cannot take effect outside a transaction (
ba68c75) - The
authz/simpleandauthz/spicedbforwarding packages will be removed in the next major release; useauthz/rbacandauthz/rebac/spicedb(dac2d76)
Fixed¶
- SCIM user and group listing panicked on every ascending sort, including the default order (
7f0e320) - The identity/oauth Fosite storage dropped any subject that was not a UUID; tokens and authorization codes now record the subject (
16f7f9f) - CreateClient reported every constraint failure, such as a nonexistent owner, as ErrClientExists; it now does so only for a duplicate client ID (
57ece18) - Federation sync no longer discards lookup errors, and a delete reports synced only for an identity that was never linked (
d054dc7) - rls.SetupTestTenant and SetupTestUser inserted into tables that no longer existed; they now create organizations, human principals, and principal memberships, and report cleanup failures (
40fc9b9) - rls.SystemForgeTables named a table that no longer existed, so MigrateSystemForge failed; it now names sf_principal_memberships (
012db98) - identity/oauth.NewProvider panicked on construction because its storage no longer satisfied Fosite's interfaces; it now does, checked at compile time (
5e36fc3) - identity/oauth never recorded a token family, so revoking refresh and access tokens had no effect; tokens record their request as the family and rotation retires the exchanged refresh token (
5e36fc3) - SystemAuth failed to record client-assertion JWT IDs under foreign-key enforcement, breaking private_key_jwt client authentication and leaving assertions replayable (
bb78cf8) - Changing a user's email through SCIM no longer keeps the previous address's verification (
ce3ea97) - The marketplace, organization, and invite APIs panicked at registration because list filters used pointer query parameters (
52be74e) - The simple provider denied every organization permission check, because its permission names did not match the ones it looked up (
fdfe9eb) - SpiceDB GetRole reported editors as non-members (
8762bc6) - A managed principal's first sign-in to a relying party failed for lack of a verified email (
b6f214a) - In-memory stores, password login, and federation sync could match or adopt a human by an empty email (
ca80678) - Invite emails are matched case-insensitively (
236c0e9) - organization.Service.Create failed for an organization without a plan instead of defaulting to free (
6b3f405) - GetMembership reports a missing membership as organization.ErrMembershipNotFound (
faedc5a) - identity/credential reports failures to record a credential's use and corrupt stored hashes (ErrCorruptCredential) instead of discarding them, and CreatePassword no longer creates a second password when its lookup fails (
43ac1ff) - identity/oauth's authorization code exchange always failed and its tokens lost their subject; its grant storage now delegates to SystemAuth's, enforcing PKCE and redirect URIs and detecting code replay and refresh-token reuse (
f2f71da) - A signup that failed after creating the user left an account with no organization whose email could not sign up again (
e6d07df) - principal type-specific updates changed shared fields before checking the type, and CreateAgent accepted a missing or inactive delegating principal (
5e002b3) - identity/oauth.NewAPI panicked on every call because the Fosite interceptor was added after routes (
ef7ea3d) - The federation lookup handler read its path parameter with net/http's PathValue, which chi does not populate (
d57bf0f) - The memory rate-limit store panicked when used after Close, and discarded windows after a fixed hour whatever their period (
3019061) - A login with no usable characters in its username gets a neutral slug (
7eaa539) - Accepting an invite syncs the new membership to the authorization graph (
47b0cd9) - Principals and organizations created by social login are registered with the authorization syncer (
d3566cf) - Signup creates the principal and personal organization in one transaction (
cc58377) - The application-role seat limit is checked under a lock so concurrent assignments cannot exceed it (
8521c37) - A managed principal is never a valid manager (
88b8c7b) - Authorization checks for AddManager, RemoveManager and Release run inside the row-locked transaction (
66ff372) - Managing operations record the accurate access-change reason (
d94f453) - The resource schema helper keeps
owneranddelete(3fba5d6) - Assigning an application role with no seat left answers 409 instead of 500 (
cbb810e)
Dependencies¶
- 1 dependency update
Documentation¶
- ADRs live in
docs/design/adrs/with an index of statuses, under a Design section in the docs nav; ADR-006 (organization lifecycle), ADR-007 (memberships and entitlements as claims), ADR-008 (resource-scoped collaborators, with rbac/rebac guidance) and ADR-009 (management is a relationship) are added (4ee73c5) - ADR-004 (retire legacy User; Principal as the sole identity root) and ADR-005 (managed principals and manager relationships), with specs for INIT-SYSTEMFORGE-007 and INIT-SYSTEMFORGE-008
- Identity, OAuth, integration, and getting-started documentation rewritten for the principal model; the schema reference is generated from the Ent schema
- The service-accounts page states that the RFC 7523 JWT-bearer grant is not implemented; the migration guide states that only Argon2id password hashes can be imported
- Resource servers accepting AI agents via AAuth (
00b495f) - Managed principals guide, with the email exception, login token security, and base manager relations added to the users, account security, and authorization conventions docs (
6f9cd36) - Architecture pages for organizations, managed principals and authorization describe the implemented behavior (
287ffaf) - Guides for invites, application roles and entitlements, collaborators and resource grants, organization slugs, personal-to-team conversion, access-change invalidation and the authorization mode (
940fd7e) - Feature specifications moved from docs/design to docs/specs (
30d9c8f) - INIT-SYSTEMFORGE-011 specifications for the organizations initiative (
f41140a)
Tests¶
- The test person-server client uses an explicit HTTP client (
e0f1773)
v0.13.0 - 2026-10-06¶
Highlights¶
identity/systemauthsvc: embed SystemAuth in a host binary with config loading, production checks, migrations, client bootstrap and health- Production safety checks enforced by
systemauthsvc.New, not only by the command Service.Metadata()lets in-process relying parties skip network discovery- Configuration overrides apply before defaults and validation; SystemAuth configuration can be loaded from bytes
Added¶
Server.OpenIDConfiguration()andServer.Ready(ctx)on the SystemAuth server (d4e1834)identity/systemauthsvcexported SystemAuth service setup for host binaries (77e642b)systemauth.DecodeConfigandDecodeConfigFiledecode configuration without validating (af3891e)systemauthsvc.LoadConfigBytes;LoadConfigvalidates after overrides (974b8b0)
Changed¶
cmd/systemauthbuilds the server throughsystemauthsvc; flags and defaults unchanged, production-refusal message reworded (adf9268)
Documentation¶
- Verified email design: ADR-003 and INIT-SYSTEMFORGE-006 (
9786049) - Embedding SystemAuth in a host binary (
7a7e5f5) LoadConfigBytesand override ordering (f52d8f2)
v0.12.0 - 2026-10-06¶
Highlights¶
- Social login on SystemAuth: GitHub/Google login routes, principal upsert keyed by provider subject with verified-email linking, hardened
__Host-sf_loginsession, state CSRF + PKCE, allowlisted redirects - Session and token lifecycle: refresh-token rotation with reuse detection and absolute expiry, logout with token revocation, consent page
- OpenID Connect: ID tokens, optional RS256 JWT access tokens, and the
/oauth/userinfoendpoint identity/relyingparty: reusable OIDC relying party withsub→sf_principal_idlinking,/bff/*cookie sessions, and bearer middleware for JWTs and API keys- Durable stores: Ent-backed SystemAuth login sessions, login state and consent grants;
identity/relyingparty/pgstoreencrypted PostgreSQL session stores with key rotation - Production-deployable
systemauthbinary: PostgreSQL via pgx, signing key from a secret, health checks, and a production mode that refuses unsafe configuration
Breaking¶
- Removed the unused duplicate
session/oauthpackage; useidentity/oauthclient(51f85fd) cmd/systemauthwithout--devrefuses to start without a signing key, an https issuer, and a persistent database, and rejectsinsecure_cookies(bd65215)- Refresh tokens rotate on every use; presenting a rotated refresh token returns
invalid_grantand revokes the token family (2c72979) - OAuth codes and tokens record a
subject; auth-codeuser_idbecomes nullable; runsystemauth migrate(6ed9b76)
Added¶
identity/oauthclient: providerConnector,User.EmailVerified, and a pluggable single-useStateStore(178063d)ExternalIdentityentity (sf_external_identities) linking principals to upstream logins (9d2f589)- SystemAuth principal directory and external-login account linking (verified-email linking only) (
ae8a363) social_loginconfiguration with redirect allowlist (9ae2eb5)__Host-sf_loginsession store and hardened cookies (35b8ba3)- GitHub/Google login routes on SystemAuth (
a1e4310) - Logout and consent pages (
3644e1b) - OpenID Connect UserInfo endpoint (
adbb5b7) - OIDC ID tokens, optional JWT access tokens, and
idp_hint(d83b627) identity/relyingpartyOIDC client and principal linking (2f2ef88)- Relying-party
/bffcookie-session handler and bearer middleware (c0c6f5d) - Signing key loaded from a secret with a key ID, and
/healthz//readyz(d496386) - Production-deployable
systemauthbinary with PostgreSQL via pgx and env/flag configuration (bd65215) - Ent schemas for login sessions, login state and consent grants (
52b21a5) - Ent-backed SystemAuth login session, login state and consent stores, used by default with database storage (
311ce3a) identity/relyingparty/pgstorePostgreSQL session and login-state stores with AES-256-GCM encryption at rest (0369dd5)pgstore.Keys,KeysFromEnv, andNewStoresfor shared session-key loading and rotation (64bde37)
Changed¶
- Relying-party memberships come from a
MembershipSourcefed by SystemAuth claims; sessions tracksidfor future back-channel logout (f34b05d) cmd/systemauthlogs whether social login state is database-backed at startup (b286d70)
Fixed¶
- Refresh-token rotation with reuse detection; reused refresh tokens no longer crash the server (
2c72979) - Query-escape the return URL in
DefaultSessionProviderredirects (50743ec) - Update configured OAuth clients that already exist in the database on startup (
ca517d3) - Generate the relying-party seal nonce in a dedicated buffer (sealed format unchanged) (
fcce545)
Dependencies¶
- 1 dependency update
Documentation¶
- Centralized social login design: ADR-002 and INIT-SYSTEMFORGE-005 (
66a0103) - Social login guide (
c897f21) - Logout, consent, and refresh-token rotation (
db3ced2) - SystemAuth endpoints and UserInfo (
d999cd7) - Federating applications to SystemAuth (relying-party guide) (
13afdf0) - SystemAuth production deployment guide (
6e5f979) - Production stores for SystemAuth and relying parties (
ec75da4) NewStoresand environment-based key rotation (b3dda5d)
v0.11.0 - 2026-10-02¶
Highlights¶
- coreauth -> systemauth: the identity/auth server package, binary, bootstrap user, JWKS key id, env var, and observability metric namespace all rename; the parallel CoreControl naming is retired in favor of the same name
- cf_ table prefix -> sf_: all 26 identity tables, via the Ent schema annotations
- API-key visible prefix cf_ -> sf_: newly issued keys read sf_live_.../sf_test_...
- Unified cross-app principal-link field: PrincipalMixin's sf_principal_id replaces the divergent core_control_principal_id / core_auth_principal_id spelling
Breaking¶
- identity/coreauth -> identity/systemauth import path; cmd/coreauth -> cmd/systemauth binary; CoreControlConfig and related types renamed to SystemAuthConfig etc. (
d97bdde) - All cf_ tables renamed to sf_; migrations/0001_rename_cf_to_sf.sql provided for an existing dev database with data to keep (
5f78037) - identity/apikey.DefaultPrefix and identity/credential.APIKeyPrefix move from cf_ to sf_; dev-issued cf_-prefixed keys must be reissued (
385fe99) - PrincipalMixin's SSO-federation link field renamed to sf_principal_id; consumers on core_control_principal_id or core_auth_principal_id must rename and regenerate Ent (
5734a60)
Documentation¶
- README Database Tables and Migration Strategy sections updated to sf_*
v0.10.0 - 2026-09-06¶
Highlights¶
- Module path moved from
github.com/grokify/systemforgetogithub.com/plexusone/systemforge; the GitHub repository was transferred to the plexusone org - Cross-app authorization vocabulary contract (
AppVocabulary) and registry so Forge platform apps share one role/permission/scope convention - Shared SpiceDB base schema (
principal/organization/platform) with a composition helper for app-contributed fragments
Breaking¶
- Module path changed to
github.com/plexusone/systemforge. Update imports fromgithub.com/grokify/systemforge/...togithub.com/plexusone/systemforge/...(the API is unchanged); the old path still resolves via GitHub's repository-transfer redirect but will not receive new tags (852744b)
Added¶
authz.AppVocabularycontract andVocabularyRegistryvalidating app-name format,{app}:{resource}:{verb}scope naming, role-hierarchy coverage, and{app}_-prefixed SpiceDB definitions (7886093)BaseSpiceDBSchemaandVocabularyRegistry.ComposeSpiceDBSchema, assembling the platform base plus every registered app fragment and rejecting duplicate definitions (d61bc4f)
Fixed¶
- Close a line-anchored regex bypass in SpiceDB schema-definition detection: a fragment packing two
definitions onto one physical line hid the second from both the{app}_-prefix check and the duplicate-shadowing check, while the raw fragment was still concatenated verbatim into the composed schema — letting an app silently redefine a reserved or foreign definition (3f43040) - Exclude
github.com/KimMachineGun/automemlimitv1.0.0, which removedmemlimit.SetGoMemLimitWithOptsand broke the build viajzelinskie/cobrautil/v2/cobraproclimits(a64fce1) - Reference the shared
plexusone/.githubCI workflows at@main(withcheck-latest: trueadded upstream) instead of a stale pinned tag, sogo.mod's required Go patch version resolves correctly (1e27a9a)
Dependencies¶
- 3 dependency updates
Documentation¶
- Publish the
INIT-SYSTEMFORGE-001(unified Forge authorization/IAM/shell) andINIT-SYSTEMFORGE-002(grokify-to-plexusone migration) initiative specs (fd97642)
v0.9.0 - 2026-08-23¶
Highlights¶
- DPoP (RFC 9449) extracted to the standalone
github.com/grokify/goauth/dpoppackage; consumers must update their import path - GuardSQL authorization adapter (
authzguardsql) consolidated into SystemForge - OpenAPI 3.1 spec with an interactive Scalar API reference in the docs site
Breaking¶
- The
session/dpoppackage has been removed; DPoP now lives atgithub.com/grokify/goauth/dpop. Update imports fromgithub.com/grokify/systemforge/session/dpoptogithub.com/grokify/goauth/dpop(the API is unchanged). The session/bff layer that binds DPoP to sessions remains in SystemForge (1892c0d)
Added¶
authzguardsqladapter mapping GuardSQL schemas/policies to SystemForge authorization (PolicyBuilder,ResourceBuilder,DefaultResourceBuilder); depends ongithub.com/grokify/guardsql(cc789a7)- OpenAPI 3.1 specification and a Scalar interactive API reference (
b271108)
Changed¶
- bff reverse proxy migrated from the deprecated
httputil.ReverseProxy.DirectortoRewrite(Go 1.26 SA1019), propagating the session via the outbound request context (1892c0d)
Dependencies¶
- 8 dependency updates
Documentation¶
- Add a query policy engines guide and integrate it across the docs (
434862c) - Integrate the interactive API docs into site navigation (
293eb0b) - Document the authzguardsql adapter and the guardsql rename (
ac300af) - Update README shields (
9b8c673)
v0.8.0 - 2026-06-21¶
Highlights¶
- Organization ownership transfer with transaction-safe implementation
- OmniStorage session backend with Redis support and size limits
- SpiceDB API compatibility fix for v1.54.0+
Added¶
- Organization ownership transfer functionality with TransferOwnership method (
7be89d8) - POST /organizations/{slug}/transfer-ownership API endpoint (
7be89d8) - TransferOwnershipInput type for transfer configuration (
7be89d8) - OmniStorage session store backend with memory and Redis support (
3493416) - Session size limits and per-user session limits with auto-eviction (
3493416) - Site isolation for multi-tenant session management (
3493416)
Fixed¶
- SpiceDB client API compatibility with v1.54.0+ using grpc.NewClient (
4aa41ae)
Dependencies¶
- 12 dependency updates
Documentation¶
- Session management overview with architecture diagram (
b3d0cd6) - BFF sessions documentation with store interface and backends (
b3d0cd6) - Ownership transfer documentation in memberships guide (
f7218a1)
v0.7.0 - 2026-05-10¶
Highlights¶
- BREAKING: Renamed project from CoreForge to SystemForge
- ProductGraph integration for event correlation and tracking
- Session invalidation with memory and Redis stores
- Account lockout protection for identity module
Added¶
- ProductGraph client for event correlation and tracking (
cf8aa54) - ProductGraph integration with observability module (
df5c013) - Session invalidation with MemoryStore and RedisStore implementations (
b4077b1) - Account lockout protection in identity module (
500fd61)
Changed¶
- BREAKING: Renamed project from CoreForge to SystemForge - update all imports from coreforge to systemforge (
dc9b68f)
Fixed¶
- Session IsExpired flaky test on Windows due to time resolution (
e332cfa) - golangci-lint errcheck, gosec G115, staticcheck SA1019 issues (
16cfbe1) - gosec G710 open redirect and G124 insecure cookie lint issues (
d8de86f)
Dependencies¶
- 7 dependency updates
Documentation¶
- Observability overview documentation (
98b3669) - ProductGraph task tracking documentation (
9ce6db1) - ProductGraph integration design documents (
062b663)
v0.6.0 - 2026-04-12¶
Highlights¶
- Multi-app platform infrastructure for running multiple SaaS apps on shared backend
- Schema-per-app PostgreSQL isolation with X-App-ID header routing
- SystemForge identity and session integration for multiapp deployments
Security¶
- Generic 404 responses for missing/invalid X-App-ID to prevent enumeration attacks (
1c85471)
Added¶
- Multi-app server with
MultiAppModeandSingleAppModedeployment options (a6ab2aa) - AppBackend interface for composable app registration with lifecycle hooks (
a6ab2aa) - Schema-per-app database isolation using PostgreSQL schemas (
app_prefix) (a6ab2aa) - X-App-ID header routing for multi-tenant request dispatch (
a6ab2aa) - Redis and in-memory cache implementations with app-scoped prefixes (
a6ab2aa) - SystemForge identity integration with JWT claims extraction and validation (
fb0ed5f) - Session management helpers for multi-tenant deployments (
fb0ed5f) - Ent client factory for schema-isolated database connections (
fb0ed5f) - HTTP middleware for authentication and tenant context propagation (
fb0ed5f) - Example app backend implementation demonstrating AppBackend interface (
0895fe0)
Dependencies¶
- 2 dependency updates
Documentation¶
- Multiapp architecture documentation with deployment patterns (
3078737)
v0.5.0 - 2026-04-05¶
Highlights¶
- Audience-aware JWT tokens for BFF vs API client separation
- Ent-backed stores for API keys and BFF sessions with AES-256-GCM encryption
- Reusable Ent schema mixins for API keys and BFF sessions
Added¶
- Audience-aware JWT tokens with
GenerateBFFTokenPairandGenerateAPITokenmethods (8978a57) - AES-256-GCM token encryption for BFF sessions with
Encryptortype (c101b1f) - Ent-backed BFF session store with automatic expired session cleanup (
c101b1f) - Ent-backed API key store with
EntClientInterfacefor app integration (efeecfb) - APIKey Ent mixin with fields for key management (prefix, hash, scopes, environment) (
cda9b50) - BFFSession Ent mixin with encrypted token fields and session metadata (
cda9b50)
Dependencies¶
- 5 dependency updates
Documentation¶
- JWT audience separation guide for BFF vs API tokens (
4144c80) - Ent-backed session store integration guide (
4144c80) - API key Ent store implementation documentation (
4144c80) - OAuth client package documentation (
4144c80) - Design documents for JWT audience and Ent stores features (
00d784d)
v0.4.0 - 2026-03-23¶
Highlights¶
- Backend for Frontend (BFF) package with session management, rate limiting, and API proxy
- Public profile fields for users and organizations enabling directory listings
- Huma framework integration for OpenAPI schema generation in BFF endpoints
Added¶
- BFF HTTP handler with session creation, API proxy, and CSRF protection (
76b618a) - Token bucket rate limiter with per-endpoint overrides and standard headers (
3d4962b) - Client IP extraction with Cloudflare and proxy header support (
4b60e87) - Huma API integration for BFF with OpenAPI schema generation (
02c57fc) - Public profile fields for HumanMixin (slug, headline, bio, social links) (
a4a52b7) - Public listing fields for OrganizationBase (tagline, description, website) (
156ff92)
Dependencies¶
- 3 dependency updates
Documentation¶
- BFF pattern documentation with architecture and integration examples (
92590b0)
Infrastructure¶
- Updated shared CI workflows to Go 1.26.x only (
126ca83)
v0.3.0 - 2026-03-14¶
Highlights¶
- Vendor-agnostic observability integration with OmniObserve (Datadog, New Relic, Dynatrace, OTLP)
- SaaS marketplace infrastructure with Stripe billing and seat-based licensing
- Redis-backed rate limiting with sliding window and token bucket algorithms
- OAuth client management and provider abstraction for CoreAuth
Added¶
- Observability package with OmniObserve integration for metrics, traces, and logs (
8b08c3f) - Marketplace package with Stripe subscription and seat-based licensing (
736cca3) - OAuth client management service in
identity/oauthclient(2bd40be) - CoreAPI rate limiting package with policy-based controls (
be156d1) - Redis-backed rate limiter with sliding window algorithm (
4de27f5) - Marketplace Ent entities: License, Listing, Subscription, SeatAssignment (
daed5ba) - CoreAuth provider abstraction layer for pluggable authentication (
6e3698d) - JWT claims enhancement with audience and issuer validation (
7e343ee) - Session middleware improvements with context utilities (
0ab89cf)
Dependencies¶
- 3 dependency updates
Documentation¶
- Authorization PRD and TRD design documents (
cc4e0cd) - Marketplace PRD design document (
cc4e0cd) - CoreAPI and CoreAuth documentation (
cc4e0cd)
v0.2.0 - 2026-03-08¶
Highlights¶
- SpiceDB authorization provider for Zanzibar-style relationship-based access control (ReBAC)
- Principal-based identity model supporting humans, agents, applications, and service principals
- SCIM 2.0 user provisioning with SystemForge-compliant patch operations
- Identity-authorization sync automatically maintains SpiceDB relationships on identity changes
Added¶
- SpiceDB authorization provider with embedded and remote modes (
1bdab84) - Principal identity model with type-specific extensions (Human, Agent, Application, ServicePrincipal) (
beb1dba) - GitHub-style organization system with owner, admin, member roles (
f10369c) - SCIM 2.0 user provisioning endpoints with filter, patch, and bulk operations (
e0da6c7) - Coreauth authentication server with Fosite-based OAuth 2.0 (
0462153) - Credential and token management service for principals (
b7cbbd7) - Standardized API contract module with consistent error handling (
2f78fae) - Coreauth CLI for server and key management operations (
af33be0) - User schema extended with profile fields (locale, timezone, avatar) (
4c300f5) - RelationshipSyncer interface for identity-authorization integration (
0d6b81e) - No-op syncer for deployments without SpiceDB (
0d6b81e) - SpiceDB integration tests with embedded server (
b77e366)
Changed¶
- OAuth 2.0 implementation refactored for principal-based identity (
09b1146)
Removed¶
- Casbin authorization provider (replaced by SpiceDB) (
0c01487)
Documentation¶
- SpiceDB setup guide for embedded and remote modes (
1bdab84) - SpiceDB schema reference with permission inheritance model (
1bdab84) - Authorization integration patterns and middleware examples (
0d6b81e) - Updated module documentation for v0.2.0 (
4d676b1)
v0.1.0 - 2026-02-28¶
Highlights¶
- Batteries-included Go platform module for multi-tenant SaaS applications
- Complete identity management with users, organizations, and memberships
- Full OAuth 2.0 server implementation using Fosite with PKCE, client credentials, and JWT bearer grants
- Session management with JWT, DPoP (RFC 9449) proof-of-possession, and BFF pattern
Added¶
- User accounts with email and Argon2id password hashing (
0c48782) - Organizations for multi-tenant applications with name, slug, plan, and settings (
0c48782) - Memberships with flexible role-based user-organization relationships (
0c48782) - OAuth account linking for GitHub and Google providers (
0c48782) - API key service for machine-to-machine authentication with scopes (
42ce852) - OAuth 2.0 server using Fosite with Authorization Code + PKCE grant (
91bac6c) - Client Credentials grant for service-to-service authentication (
91bac6c) - Refresh Token grant with rotation and theft detection (
91bac6c) - JWT Bearer grant (RFC 7523) for service account authentication (
91bac6c) - Service accounts with RSA/EC key pairs for non-human identities (
91bac6c) - Token introspection (RFC 7662) and revocation (RFC 7009) endpoints (
91bac6c) - JWT service supporting HS256, RS256, and ES256 algorithms (
1a09a05) - DPoP (RFC 9449) proof-of-possession token binding implementation (
1f68df0) - Backend for Frontend (BFF) pattern with server-side sessions (
bc6b81e) - GitHub and Google OAuth social login handlers (
70a85cc) - JWT Bearer and API key authentication middleware (
a15ac17) - Role-based access control (RBAC) with organization-scoped permissions (
3aed18f) - Casbin provider for advanced policy rules (
3aed18f) - Simple provider for lightweight permission checking (
3aed18f) - HTTP middleware for route protection (Chi and stdlib) (
3aed18f) - Feature flag engine with boolean, percentage, and user list flags (
d7a0bcb) - Organization-scoped feature flag evaluation (
d7a0bcb) - In-memory feature flag store for development and testing (
d7a0bcb) - PostgreSQL RLS policy generation and session variable helpers (
803db13) - Tenant isolation for multi-tenant data separation (
803db13) - Ent ORM integration with transaction helpers for tenant context (
803db13)
Documentation¶
- Comprehensive MkDocs documentation site with Material theme (
a52d6d6) - Getting started guides: installation, quickstart, configuration (
a52d6d6) - Identity module documentation: users, organizations, memberships, API keys (
a52d6d6) - OAuth 2.0 documentation: apps, authorization code, client credentials, service accounts, tokens (
a52d6d6) - Integration guides: existing apps and migration (
a52d6d6) - API reference and Ent schema documentation (
a52d6d6) - PRD and TRD design documents for authentication and OAuth (
aad6a32) - README with badges, quick start examples, and module structure (
ac04664)