jkunz 20b8d129fd
Docker tag rebuild blocked / reject-rebuild (push) Failing after 2s
v12.0.0
2026-07-25 18:53:32 +00:00
2026-07-25 18:53:32 +00:00
2026-07-25 18:53:32 +00:00
2026-07-25 18:53:32 +00:00
2026-07-25 18:53:32 +00:00

@idp.global/app

Identity infrastructure for apps that need accounts, sessions, organizations, invites, admin tooling, mobile passport approvals, security alerts, OpenID Connect, SAML SSO, and SCIM in one TypeScript codebase.

This repository ships the idp.global server, CLI, and web UI used by the hosted service. Shared public contracts live in @idp.global/interfaces; reusable browser/server SDK code lives in @idp.global/sdk.

Issue Reporting and Security

For reporting bugs, issues, or security vulnerabilities, please visit community.foss.global/. This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a code.foss.global/ account to submit Pull Requests directly.

What It Does

  • Runs an identity provider with MongoDB-backed users, sessions, roles, organizations, invitations, OIDC clients, and billing plans.
  • Serves a web app for login, registration, account management, org management, billing flows, and global admin views.
  • Exposes typed realtime APIs over typedrequest and typedsocket.
  • Implements OIDC/OAuth endpoints including discovery, JWKS, authorization, token, userinfo, and revoke.
  • Provides inbound SAML SSO with DNS-verified organization domains, JIT provisioning, group-to-role mapping, and SCIM 2.0 provisioning.
  • Supports passport-style mobile device enrollment, signed approval challenges, push registration, security alerts, and NFC/location-backed identity proof flows.
  • Includes a reusable browser client and a terminal CLI for common account and org workflows.

Monorepo Modules

Folder Purpose
ts/ Backend service entrypoint and the core Reception managers
ts_idpcli/ CLI published as @idp.global/cli
ts_web/ Frontend bundle with login, registration, account, org, billing, and admin views
../interfaces/ Shared request and data contracts published as @idp.global/interfaces
../sdk/ Browser and server SDK published as @idp.global/sdk

Core Backend Pieces

Reception wires the service together and starts these managers:

  • JwtManager for signing, refreshing, and validating JWTs.
  • LoginSessionManager for login state and session lifecycle.
  • RegistrationSessionManager for multi-step sign-up flows.
  • UserManager for user lookups and account data.
  • OrganizationManager for org creation and membership lookup.
  • RoleManager for org roles and permissions.
  • UserInvitationManager for invites, membership updates, and ownership transfer.
  • BillingPlanManager for Paddle-backed billing data.
  • AppManager and AppConnectionManager for app connections and admin app stats.
  • AuditManager for privacy-safe structured events, integrity verification, and durable append-only sink delivery.
  • AlertManager for passport alerts and organization/global alert rules.
  • AbuseProtectionManager for keyed, owner-fenced rate limits on login, recovery, MFA, and OIDC token exchange flows.
  • PassportManager and PassportPushManager for trusted device enrollment, challenge approval, and push notification delivery.
  • OidcManager for the OIDC/OAuth provider surface.
  • EnterpriseSsoManager for SAML metadata, login, assertion validation, DNS domain ownership, and JIT provisioning.
  • ScimManager for org-scoped SCIM users, groups, and bearer-token management.

Quick Start

Prerequisites

  • Node.js 22.22.1
  • pnpm 11.15.0
  • MongoDB

Install

pnpm install

Required Environment

export MONGODB_URL=mongodb://localhost:27017/idp-dev
export IDP_ENV=local
export IDP_BASEURL=http://localhost:2999
export INSTANCE_NAME=idp-dev
export SERVEZONE_PLATFORM_AUTHORIZATION=test
export IDP_TOTP_ENCRYPTION_KEY=replace-with-at-least-32-characters

Startup validates a canonical runtime config before Reception starts. IDP_ENV accepts production, development, test, or local; any other value is rejected. When IDP_ENV is unset, the app defaults to production behavior.

IDP_BASEURL is the canonical public URL for OIDC issuer metadata and generated registration, login, password-reset, welcome, and invitation links. Production requires HTTPS. Local HTTP is only accepted with explicit IDP_ENV=local, development, or test, and only for localhost-style hostnames.

SERVEZONE_PLATFORM_AUTHORIZATION is required. The literal value test switches the platform client to debug mode for local development and tests and is rejected in production.

IDP_TOTP_ENCRYPTION_KEY is required in every runtime mode and must be at least 32 characters.

Production-required backend service token:

  • IDP_BACKEND_TOKEN — shared secret authenticating backend services on the getPublicKeysForValidation and pushOrGetJwtIdBlocklist typed endpoints. It is production-required and must be at least 32 characters. In non-production it can be omitted, and those endpoints remain fail-closed.

Optional:

  • PADDLE_TOKEN
  • PADDLE_PRICE_ID

Build

pnpm build

Run Locally

pnpm watch

This starts the backend watcher from ts/ and rebuilds the frontend bundle from ts_web/. The packaged executable constructs the production structured-audit runtime from environment configuration and fails closed unless OpenBao custody, Lossless object-storage retention, and the serve.zone dead-letter route all qualify. Tests inject the deterministic in-memory runtime explicitly. Do not use that test runtime for shared or production deployments.

Seed Development Data

pnpm run seed

The seed command starts an interactive CLI that writes to the configured local database. The default demo workspace creates a global admin, an organization, demo users, and global OAuth app records.

This seed path is development-only. Never run it against a production database; production first launch uses the explicit bootstrap described below and creates no demo records.

Default development credentials if accepted unchanged:

  • Email: admin@idp.global
  • Password: idp.global

Those credentials are for disposable local instances only. Never reuse them for production or any shared environment.

Runtime Surface

Web Routes

Route Purpose
/ Welcome page
/login Login flow
/logout Logout flow
/register Registration flow
/finishregistration Multi-step registration completion
/dash Signed-in account area and account subroutes

OIDC and OAuth Endpoints

Route Purpose
/.well-known/openid-configuration Discovery document
/.well-known/jwks.json Public signing keys
/oauth/authorize Authorization endpoint
/oauth/token Token exchange
/oauth/userinfo UserInfo endpoint
/oauth/revoke Token revocation

Supported scopes in the OIDC manager include openid, profile, email, organizations, and roles.

SAML SSO And SCIM

Route Purpose
/saml/login/:connectionId Start an SP-initiated SAML login for a connection with a verified domain
/saml/acs/:connectionId Validate the signed SAML response and complete account linking or JIT provisioning
/saml/sp/metadata/:connectionId Publish service-provider metadata for customer IdPs
/scim/v2/* Org-scoped SCIM 2.0 discovery, user, and group endpoints

Organization admins configure SAML and SCIM under the SSO & Provisioning view. Every SSO email domain starts pending with a generated DNS TXT challenge. Home-realm discovery, SAML login, existing-account linking, and JIT provisioning remain disabled for that domain until the authoritative claim is verified. Domain claims are globally unique across organizations.

The implemented SCIM profile is deliberately bounded:

Capability Declared support
Authentication and tenancy Organization-scoped bearer tokens; every resource lookup and mutation is fenced to the token organization
Discovery ServiceProviderConfig, ResourceTypes, and Schemas
Users Create, retrieve, list, replace, patch, and deprovision; deactivated users remain retrievable
Groups Create, retrieve, list, replace, patch membership, and delete
Filters Equality only: users accept userName, emails.value, externalId, and id; groups accept displayName, externalId, and id
Pagination One-based, default 100, maximum 200, stable database ordering; count=0 returns counts without resources
Versioning Weak ETags in the HTTP ETag header and resource meta.version; single-resource GET supports If-None-Match/304, and PUT, PATCH, and DELETE accept optional If-Match/412 preconditions
PATCH compatibility Case-insensitive operations, Entra boolean strings and no-path dotted user attributes, plus Okta/Entra group-member add, replace, and value-path removal
Conflicts Per-organization user name/email, group display name, and external-ID uniqueness return SCIM 409 errors
Not supported Bulk, sorting, password change, POST search, arbitrary filter grammar, or undeclared PATCH behavior

Lists use explicitly hinted compound indexes that support their stable sort, database pagination, and batched user/member projection reads. Real-Mongo coverage seeds 320 users, groups, memberships, and tokens per tenant; first-page document reads are bounded by the requested page size, member expansion uses one link batch plus one user batch, token hashes resolve through one indexed document, and organization token reads remain tenant-scoped. Production scale and interoperability qualification still require the exact pinned Corestore digest plus the Okta and Entra evidence in readme.plan.md.

SCIM bearer-token records use a globally unique token-hash authority, exact revisioned inserts and revocation CAS, monotonic no-upsert usage telemetry, and exact organization-lifecycle deletion. Plaintext remains one-time output only.

Privileged administration uses declared default-deny capabilities for global OIDC clients, organizations, alert rules, users, global-administrator lifecycle, and signing-key rotation, plus tenant-scoped organizations, roles, memberships, app connections, SSO, SCIM tokens, billing, and alert rules. Entry authorization requires an active persisted JWT/session/user chain and, for tenant access, exactly one current role in the target organization. Existing mutation fences recheck live actors for OIDC clients, organization suspension, global user suspension and administrative purge initiation, global-administrator lifecycle, signing-key rotation, and tenant mutations. Global-administrator grant and removal additionally require an exact reviewed user revision, caller-stable operation ID, distinct actor and target, fresh user-verified passkey, one shared last-active-administrator fence, and purpose-bound attempted plus terminal audit events. Administrative user purge, organization suspension, and signing-key rotation use the same two-minute passkey class and reassert the persisted session inside their mutation fences. Tenant invitation creation, cancellation, resend, and bulk creation plus member removal, member-role replacement, and ownership transfer use the five-minute passkey class and reassert both the same persisted session and live tenant capability inside their mutation fences.

Direct platform capability assignments are limited to the declared delegable subset and active non-administrators. Changes require an exact User revision, caller-stable operation ID, distinct actor and target, a passkey authenticated within two minutes, mutation-time reauthorization, and attempted plus terminal audit. Promotion and suspension clear direct assignments. Custom organization roles carry sorted, unique delegable tenant capabilities; malformed definitions fail closed, and organization deletion or ownership transfer cannot be delegated.

Emergency platform access is a separate break-glass lifecycle. An active non-administrator with the direct global.break-glass.request capability can request only global.oidc-client.manage, global.organization.manage, global.alert.manage, or global.user.manage for 60 through 1800 seconds. Requests use an exact User revision, caller-stable operation ID, a closed justification code, and a passkey authenticated within two minutes. A distinct live global administrator must approve within ten minutes. The grant records custody-protected approver identity, expires automatically, records its first use, emits structured audit and high/critical global alerts, and must be closed with a durable confirmed or escalated post-use review. It never grants global administrator management, account deletion, signing-key rotation, or approval authority. Promotion to global administrator is blocked while a request or grant remains open, and review immediately terminates an active grant.

Custom-role definition changes use that same tenant-privilege class. App connection and role-mapping changes, SSO connection and domain-verification changes, and SCIM bearer-token creation or revocation use the five-minute tenant-security class; each reasserts the persisted session, active organization, and live capability inside its guarded mutation. Organization settings use the five-minute tenant-privilege class. Organization deletion uses the five-minute tenant-security class, binds resumable work to the initiating owner, and reasserts a current eligible user plus fresh persisted passkey session before every deletion stage and finalization boundary.

Tenant billing changes and global or tenant alert-rule changes use separate five-minute passkey classes. Billing reasserts the same persisted session, active organization, and live billing capability inside its organization fence. Alert-rule creation, update, and deletion reload and recheck the same strong global or tenant authority inside the existing actor, recipient, and organization fences before persistence.

Confidential global OIDC client creation, client-secret rotation, and delivery acknowledgement require a passkey authenticated within five minutes. The caller supplies a canonical RSA-3072 or RSA-4096 OAEP-SHA-256 public key and stable operation ID. The server atomically persists only the secret hash plus an encrypted, actor/request-bound delivery receipt; exact retries return the same ciphertext and cannot rotate twice. The browser SDK verifies and decrypts the envelope with its caller-held non-exportable private key. Exact acknowledgement then removes the ciphertext irreversibly while retaining a compact idempotency tombstone. No confidential creation or rotation response contains plaintext.

Password and login-identifier changes, TOTP disablement, backup-code regeneration, passkey registration or revocation, and Passport device enrollment or revocation require a passkey authenticated within five minutes. The same persisted session is reasserted inside the credential or identity mutation fence. To avoid a first-factor deadlock, a password, magic-link, or federated primary session may enroll only its first passkey or TOTP factor when that primary authentication is less than five minutes old; refresh activity does not renew this window. Password recovery remains a separate proof path and does not qualify as passkey step-up. A recovery request must carry exactly the current account-bound, ten-minute, one-time reset token and a 15128 Unicode code-point password, normalized with NFC. Under the account identity fence, the server consumes that proof, invalidates every email-action token and pending user-bound MFA/WebAuthn challenge, revokes all platform sessions, JWTs, and OIDC state, and records the revocation before writing the new password. It then sends a security notification by both email and the recovered account's enrolled Passport push channel without disclosing reset credentials. Every new password in registration, authenticated change, or self-service recovery is checked against the HIBP Pwned Passwords range API. Only the first five hexadecimal SHA-1 characters leave the service, padded range responses are requested and cached for one hour, and provider or response-integrity failure rejects the change. Passkeys, TOTP, backup codes, and Passport devices remain enrolled recovery factors.

Operational Health Endpoints

These unauthenticated endpoints are intended for load balancers, orchestrators, and Docker runtime checks.

Route Methods Status Purpose
/healthz GET, HEAD 200 Process liveness once the HTTP server can respond
/livez GET, HEAD 200 Liveness alias for orchestrators that use live/readiness naming
/readyz GET, HEAD 200 only in the ready lifecycle state with live MongoDB and audit-provider evidence, otherwise 503 Readiness for routing traffic to the instance

GET responses use Cache-Control: no-store and return JSON with ok, status, service, and version. Readiness reports starting, ready, draining, failed, or stopped; new business requests receive 503 outside ready.

The Docker image HEALTHCHECK probes http://127.0.0.1:2999/healthz so a deliberately draining instance is not mistaken for a dead process. Load balancers and orchestrators must use /readyz for traffic admission.

Passport And Mobile Approval Flow

PassportManager powers the trusted-device side of idp.global. A web session can create a passport enrollment challenge, the Swift app completes enrollment through a QR/NFC pairing payload, and later sign-in or identity checks can be approved by the paired device with signed challenge responses.

The typed request surface includes:

  • createPassportEnrollmentChallenge and completePassportEnrollment for pairing a trusted device.
  • getPassportDevices and revokePassportDevice for account-level device management.
  • createPassportChallenge, approvePassportChallenge, rejectPassportChallenge, and listPendingPassportChallenges for approval flows.
  • getPassportDashboard, listPassportAlerts, and markPassportAlertSeen for mobile app dashboards and notifications.
  • registerPassportPushToken for push delivery setup.

Passport devices, challenges, and nonce attempts use exact runtime-validated records under the live-user/account-purge fence. Enrollment and terminal challenge retries are idempotent, devices become operational only after their approved enrollment binding exists, public keys remain globally reserved after revocation while the device record is retained, and nonce replay state covers the full accepted clock-skew window. Challenge push hints use expiring delivery leases with accepted at-least-once retry after an uncertain outcome. Current account authentication eligibility is rechecked inside the fence before new or pending enrollment/challenge grants, signed-device access, or challenge push delivery; exact terminal retries may still repair their stable audit event. Direct deletion is reserved for exact nonce expiry and the account-purge cascade.

MFA And Passkeys

The reception backend supports real multi-factor authentication for account logins:

  • TOTP enrollment with startTotpEnrollment and finishTotpEnrollment.
  • Hashed one-time backup codes through regenerateBackupCodes and verifyMfaChallenge.
  • WebAuthn passkey registration, revocation, passwordless login, and MFA step-up through the startPasskey* and finishPasskey* request pairs.
  • Password and magic-link logins return twoFaNeeded, mfaChallengeToken, and availableMfaMethods instead of a refresh token when MFA is configured.

Every persisted login session carries closed server-derived evidence for its primary authentication method/time and exact MFA factor/time. Refresh and transfer-token activity can update lastActive, but cannot advance either authentication timestamp. The central authorization authority can require a fresh user-verified passkey using the approved two- or five-minute operation class. TOTP and backup codes are recovery factors and do not qualify for global administration; no managed-device strong factor exists yet.

Authentication-method changes use the five-minute passkey class. The only exception is narrowly scoped first-factor bootstrap from a primary authentication less than five minutes old while the relevant active factors do not yet exist. Pending Passport enrollment records retain the exact authorizing session internally until device creation; serializers never expose that binding.

Global-administrator grants and removals use setGlobalAdmin. Callers first review IGlobalUserAdminDto.revision, then submit that exact revision with a caller-stable operationId. The server requires a distinct active global administrator with a passkey authenticated within two minutes, serializes all lifecycle decisions, preserves at least one active administrator, and emits durable attempted plus terminal audit outcomes. Global administrators must have their authority removed by another administrator before deleting their own account. Administrative account purge, organization suspension, and OIDC signing-key rotation use the same two-minute passkey class. Rotation first persists a caller-stable request bound to the reviewed active key and expires it after ten minutes without changing signing material. A different active global administrator must approve with a separately fresh passkey session. The server locks and revalidates both administrators, then atomically rotates the key and closes an exact custody-tokenized retry receipt in the keyring CAS. Request and terminal outcomes are purpose-bound in the protected audit trail.

A global administrator who has lost all passkeys may request one controlled replacement-passkey enrollment from a fresh TOTP or backup-code session. The request is exact-revision and caller-stable, expires after ten minutes, and must be approved by a distinct active global administrator using a passkey authenticated within two minutes. Approval opens a 15-minute grant bound to the requesting session. The first replacement passkey closes the grant atomically; retry reconciliation cannot enroll a second passkey. Request, activation, completion, expiry conflicts, demotion, and suspension share the administrator lifecycle fence and emit structured audit plus global security alerts.

TOTP secrets are AES-GCM encrypted. IDP_TOTP_ENCRYPTION_KEY is required and must stay stable across deployments so enrolled credentials remain decryptable.

Domain Surfaces

  • https://idp.global is public and remains the permanent issuer, discovery, OIDC, SAML, and SCIM origin.
  • https://login.idp.global is public and hosts login, registration, MFA, consent, recovery, and invitation flows.
  • https://app.idp.global is private and hosts the dashboard, full application API, and administrative CLI.

SDK Example

The private application shell uses the dedicated SDK browser entrypoint published by @idp.global/sdk. Public relying parties should integrate through dynamic OIDC discovery at https://idp.global/.well-known/openid-configuration, not the private TypedRequest API.

import { IdpClient } from '@idp.global/sdk/browser';

const idpClient = new IdpClient(
  'https://app.idp.global',
  { appUrl: 'https://app.idp.global/' },
  { loginBaseUrl: 'https://login.idp.global' },
);
await idpClient.enableTypedSocket();

const isLoggedIn = await idpClient.determineLoginStatus();

if (!isLoggedIn) {
  const loginResult = await idpClient.requests.loginWithUserNameAndPassword.fire({
    username: 'user@example.com',
    password: 'correct horse battery staple',
  });

  if (loginResult.refreshToken) {
    await idpClient.refreshJwt(loginResult.refreshToken);
  }
}

const whoIs = await idpClient.whoIs();
console.log(whoIs.user.data.email);

CLI Example

The terminal client lives in ts_idpcli/ and is published as @idp.global/cli.

idp login
idp whoami
idp orgs
idp members --org <org-id>
idp invite --org <org-id> --email user@example.com

The CLI defaults to private https://app.idp.global, requires hub/VPN access, stores credential secrets in the local OS keyring, keeps only non-secret metadata in ~/.idp-global/credentials.json, and reads IDP_URL to override the target server.

Shared Interfaces

The sibling @idp.global/interfaces package exports the type contracts shared across the stack:

  • data/* for users, orgs, roles, JWTs, sessions, devices, billing plans, apps, passport records, alerts, OIDC, SAML, and SCIM payloads.
  • request/* for auth, registration, user, org, invitation, app, admin, billing, JWT, passport, alert, OIDC, SAML, and SCIM request contracts.
  • tags/* for shared tag exports.

Frontend

ts_web/ is the web application bundle. It contains:

  • Login and registration prompts.
  • A registration stepper.
  • Account navigation and account views.
  • Organization creation and bulk invite modals.
  • Billing and Paddle setup views.
  • Organization SAML/SCIM configuration with DNS verification state.
  • A global admin view.

Package Scripts

Command Purpose
pnpm build Build TypeScript output and frontend bundle
pnpm watch Run backend watch mode and frontend bundle watch
pnpm test Build and run the test suite
pnpm run preflight:data Verify the exact database/schema and identity invariants; this is one subordinate check and never release authorization
pnpm run preflight:namespace -- --expect=blank|prebootstrap|stable Classify a namespace with read-only inspection, verify the expected state, and fail on read drift without installing or repairing anything
pnpm run build:docker Build one clean, configured multi-platform candidate into the project-local tSDocker registry
pnpm run digest:docker Capture and validate the candidate's canonical tSDocker digest JSON and source receipt
pnpm run test:docker Test that exact persisted candidate by expected digest without rebuilding
pnpm run verify:docker Bind native runtime, image-config, platform-manifest, and embedded supply-chain evidence to the exact candidate digest
pnpm run seal:docker-evidence Revalidate and print the deterministic technical evidence seal; it explicitly does not authorize release
pnpm run supplychain:evidence Generate deterministic CycloneDX, dependency-license, root-license, and missing-license evidence for the current platform

Current-Schema and Data-Preflight API

The backend entrypoint exports CurrentSchemaManager, currentSchemaManifest, currentSchemaManifestDigest, currentSchemaVersion, the identity-preflight analyzers, the count-only release-data preflight functions, and the namespace-preflight API: namespacePreflightExpectations, runNamespacePreflight, and evaluateNamespacePreflight. Embedding and operator tooling can use these APIs to install or verify the one accepted blank/current namespace and to detect schema, identity, or held-fence blockers without logging identifiers. Namespace preflight uses read-only collection, count, schema-state, index, and data-conformance inspection. It maps expectations blank, prebootstrap, and stable to classifications blank, current-prebootstrap, and current-stable; other classifications are nonblank-unmanaged and managed-incompatible. It records before/after counts and fails if the namespace changes during observation.

Current-data verification checks the complete raw shape of all revisioned documents, Apps, the five user-bound OIDC authorization/token/consent collections, two MFA credential collections, three Passport collections, the identity-free OIDC transaction and SAML request collections, and the singleton startup controls. OIDC and Passport inserts are acknowledged and runtime-validated. Passport inserts, user-created global App writes, AppConnection creation, and OIDC consent grants are serialized against the live-user/purge fence. Apps use exact per-type validation and revision CAS; AppConnection inserts reconcile committed acknowledgement loss without inherited upserts. Consent grants and Passport lifecycle transitions use exact no-upsert CAS, while Passport telemetry uses named monotonic mutations.

These checks establish only the database contract. They do not authorize a package, tag, container, deployment, or production route opening; every gate in readme.plan.md still applies.

Docker Candidate Diagnostics

These commands are non-publishing diagnostics, not a qualified release path. They require one fresh project-scoped session ID and one clean source checkout. The wrapper rejects tracked/untracked changes and verifies that every repository-ignored path is excluded from the Docker context. Keep the same ID and checkout for the lifecycle; a new build attempt requires a new ID.

export TSDOCKER_SESSION_ID="idp-app-<source-and-run-specific-id>"
pnpm run build:docker
pnpm run digest:docker
# Run the following two commands natively on linux/amd64 and linux/arm64.
pnpm run test:docker
pnpm run verify:docker
# Run only after both native platform records are present.
pnpm run seal:docker-evidence

The canonical registry workspace remains below .nogit/docker-registry/$TSDOCKER_SESSION_ID/. The raw tSDocker digest JSON and the source/tool/test receipt remain below .nogit/release-evidence/. The receipt binds the candidate to the Git revision, package version, Dockerfile pattern, project-local tSDocker version, configured platform set, clean tag, and top-level digest. Digest capture retains the exact raw OCI index plus runnable and attestation manifests, recomputes their digests and sizes, binds each config and layer descriptor, and re-reads the project-local registry when evidence is consumed. Native verification records each host platform independently, binds the local image ID to that platform's config digest, verifies the in-image evidence artifact hashes, and reaches evidence-sealed only after both linux/amd64 and linux/arm64 name the same candidate digest. The terminal receipt and verifier output both carry releaseAuthorized: false. The generated evidence includes a normalized CycloneDX SBOM, installed production-license inventory, the root license.md hash, and a fail-closed missing-license gate.

The final successful verify:docker creates or reuses a deterministic technical evidence seal. A recoverable retry produces identical seal content; a stale receipt lock remains a fail-closed operator condition. seal:docker-evidence only revalidates and prints that existing technical seal with releaseAuthorized: false. It is not release acceptance and does not prove provenance, signatures, vulnerability-policy compliance, reproducibility, qualified notices, immutable remote publication, or deployment safety. Those gates remain mandatory in readme.plan.md.

The GitZone Docker release target is intentionally disabled. GitZone CLI 2.23 invokes a global tsdocker binary and cannot transport tSDocker 3 expected-digest evidence, so it must not own image publication. There is deliberately no release:docker script. @git.zone/tsdeploy owns production publication: it reserves a fenced Cloudly operation, builds and tests with the project-local tSDocker 3.0.0, pushes the exact version tag through an operation-scoped registry credential, requires source/configuration/index-bound provenance, SBOM, SSH signature, vulnerability-policy, and explicit release-authorization evidence, then promotes only Cloudly's independently observed immutable registry release.

The Gitea branch workflow runs the source build and regression suite only. It deliberately does not construct a container candidate or release evidence before Gate 0 reserves an unused version and candidate identity. The stable-tag workflow hard-fails instead of rebuilding: it must eventually verify retained accepted digest/evidence without constructing a new candidate.

Self-Hosting

The app ships as a single Docker image and needs a MongoDB instance next to it. Runtime configuration and database state are fail-closed. Partial, unknown, or malformed bootstrap environment configuration is rejected before the HTTP listener starts. With no bootstrap variables at all, a blank production database reaches the database/bootstrap-state gate after listener startup: /healthz and /livez remain 200, /readyz reports 503 failed, and every business/OIDC route returns 503 with Retry-After: 1 and service_unavailable until the operator stops and restarts the instance with complete bootstrap input.

The packaged executable composes its production audit runtime from environment configuration. OpenBao Transit protects audit identities, Lossless S3-compatible object storage provides the compliance-mode retention sink, and serve.zone service mail provides durable dead-letter alerting. Startup and /readyz require deadline-bounded, challenge-bound evidence for the exact configured keys, active retention lock and sentinel object, and alert route.

First Production Launch

A fresh production database never creates an administrator implicitly. After the one current-schema installer establishes and verifies the exact empty namespace, the service requires one explicit initial-admin operation. The operation creates exactly one global administrator, one organization, one owner role with manual provenance, canonical email/username ownership records, and one organization-created structured audit event. It creates no demo users, OAuth apps, API tokens, or other defaults.

Set all of these values for the first launch only:

Variable Constraint
IDP_INITIAL_BOOTSTRAP_MODE Exactly initial-admin. This is the explicit opt-in.
IDP_INITIAL_BOOTSTRAP_OPERATION_ID Stable operator-chosen id, 1128 ASCII characters. The first character must be alphanumeric; remaining characters may also use ., _, :, or -. Reuse it after an interrupted attempt.
IDP_INITIAL_BOOTSTRAP_ADMIN_NAME Display name for the initial administrator; 1200 characters with no control characters.
IDP_INITIAL_BOOTSTRAP_ADMIN_EMAIL Valid administrator email, at most 320 characters; stored in canonical lowercase form.
IDP_INITIAL_BOOTSTRAP_ADMIN_USERNAME Initial username, 1320 characters with no control characters; stored in canonical lowercase form.
IDP_INITIAL_BOOTSTRAP_ORGANIZATION_NAME Display name for the initial organization; 1200 characters with no control characters.
IDP_INITIAL_BOOTSTRAP_ORGANIZATION_SLUG Lowercase slug, 364 characters, using alphanumerics and single hyphen-separated segments.
IDP_INITIAL_BOOTSTRAP_PASSWORD_FILE Absolute path to the mounted password file described below. The password itself must never be placed in an environment variable or command argument.

All eight variables are one contract. Companions without the mode, an unknown mode, a missing companion, or malformed metadata fail configuration loading before the HTTP listener starts.

The password path must resolve directly to a regular file; symlinks are rejected. The file must be readable by runtime UID 10001, must not be group- or world-writable, and must contain 15512 bytes of valid UTF-8 with no control character or trailing newline. After NFC normalization, the password must contain 15128 Unicode code points. The reader requires operating-system no-follow support and verifies that device, inode, ownership, mode, size, and timestamps do not change while it is read. Docker secrets mounted as regular read-only files are suitable. Kubernetes projected-secret paths are symlinks by default, so expose the selected key as a regular subPath mount and pass that path instead.

Bootstrap accepts only the exact completed current-schema manifest and empty application state. Any user, organization, role, identity authority, audit entry, foreign collection or index, conflicting request, or wrong password blocks without overwriting or deleting that state. Identical concurrent calls converge on the same deterministic graph. An interrupted call must be restarted with the exact same metadata and password; only the bootstrap installer may take over its own expired lease because every effect is deterministic and forward-only.

When /readyz becomes 200, verify administrator login, stop the instance, remove every IDP_INITIAL_BOOTSTRAP_* value and the password mount, and restart. The terminal receipt is retained without its transient password verifier, normal identity data may evolve, and later startup does not need or read the bootstrap password. Keeping matching metadata configured is tolerated after completion, but removal is the required operator cleanup.

Environment Variables

Variable Required Purpose
IDP_ENV yes Runtime mode: production, development, test, or local. Anything unset defaults to production (fail-closed).
INSTANCE_NAME yes Instance identifier used in logs and metrics.
MONGODB_URL yes MongoDB connection string.
IDP_BASEURL yes Public origin (must be https:// in production; http:// only for localhost in non-production).
SERVEZONE_PLATFORM_AUTHORIZATION yes serve.zone platform binding for outbound email. test is rejected in production.
IDP_BACKEND_TOKEN production Shared secret (≥32 chars) that backend consumers present for JWT public-key and blocklist retrieval.
IDP_TOTP_ENCRYPTION_KEY yes Key material (≥32 chars) encrypting TOTP secrets at rest. Never rotate casually — enrolled authenticators become unreadable.
IDP_METRICS_TOKEN no Bearer token (≥32 chars) gating GET /metrics (Prometheus text format). Unset disables the endpoint (404).
IDP_SHUTDOWN_GRACE_PERIOD_MS no Grace period for draining admitted requests and background work before reporting a shutdown timeout (integer from 1000 through 900000; default 30000). Cleanup continues after a timeout report.
IDP_INITIAL_BOOTSTRAP_* first production launch only Complete one-shot administrator/organization configuration from the preceding section. Partial configuration is rejected. Remove all values and the mounted password file after the verified bootstrap restart.

docker-compose Example

services:
  idp:
    image: code.foss.global/idp.global/app@sha256:<recorded-release-digest>
    restart: unless-stopped
    ports:
      - '2999:2999'
    read_only: true
    tmpfs:
      - /tmp:size=64m,mode=1777
    stop_grace_period: 45s
    environment:
      IDP_ENV: production
      INSTANCE_NAME: idp-selfhosted
      MONGODB_URL: mongodb://idp:change-me@mongodb:27017/idp?authSource=admin
      IDP_BASEURL: https://idp.example.com
      SERVEZONE_PLATFORM_AUTHORIZATION: '<your serve.zone binding>'
      IDP_BACKEND_TOKEN: '<random 32+ char secret>'
      IDP_TOTP_ENCRYPTION_KEY: '<random 32+ char secret, back it up>'
      IDP_SHUTDOWN_GRACE_PERIOD_MS: 30000
      IDP_INITIAL_BOOTSTRAP_MODE: initial-admin
      IDP_INITIAL_BOOTSTRAP_OPERATION_ID: first-production-launch
      IDP_INITIAL_BOOTSTRAP_ADMIN_NAME: '<initial administrator name>'
      IDP_INITIAL_BOOTSTRAP_ADMIN_EMAIL: '<initial administrator email>'
      IDP_INITIAL_BOOTSTRAP_ADMIN_USERNAME: '<initial administrator username>'
      IDP_INITIAL_BOOTSTRAP_ORGANIZATION_NAME: '<initial organization name>'
      IDP_INITIAL_BOOTSTRAP_ORGANIZATION_SLUG: '<initial-organization-slug>'
      IDP_INITIAL_BOOTSTRAP_PASSWORD_FILE: /run/secrets/idp-bootstrap-password
    secrets:
      - idp-bootstrap-password
    depends_on:
      - mongodb
  mongodb:
    image: mongo:8
    restart: unless-stopped
    environment:
      MONGO_INITDB_ROOT_USERNAME: idp
      MONGO_INITDB_ROOT_PASSWORD: change-me
    volumes:
      - mongodb-data:/data/db
volumes:
  mongodb-data:
secrets:
  idp-bootstrap-password:
    file: ./secrets/idp-bootstrap-password

Create the password file without a trailing newline and protect the host copy before starting Compose. After the successful bootstrap verification, remove the eight bootstrap environment entries and the service secret mount, then restart the service. Use only a released immutable digest; the placeholder above must be replaced before deployment.

Health endpoints for orchestration: GET /healthz (liveness), GET /readyz (readiness; reports the running version and lifecycle state). The image ships a Docker HEALTHCHECK probing /healthz; configure traffic admission against /readyz.

Rollback by Digest

Record the immutable multi-architecture digest and workload tag for every release. Roll back by selecting that recorded immutable artifact in the orchestrator; never move or retag latest.

Cloudly deployments must follow the release and rollback runbook. The first production launch has no legacy-data upgrade or cutover. Before public traffic is enabled, rollback means keeping routing closed, returning to the recorded immutable artifact, and either retaining the untouched empty target or restoring the fresh post-bootstrap backup proven by the isolated restore rehearsal. After real production data exists, every later release must declare its own data-compatibility and backup requirements; an image rollback must never be assumed to reverse schema or data changes.

Observability

  • Production logs are JSON lines on stdout (one object per log event, including commitinfo context). Non-production modes log human-readable text.
  • Every handled typed request logs method, request id (correlation id), duration and error class. Payloads are never logged.
  • Raw HTTP endpoints honor an incoming x-request-id header and echo it on the response.
  • GET /metrics (with Authorization: Bearer $IDP_METRICS_TOKEN) exposes process and system metrics in Prometheus text format.

Operational runbooks (health monitoring, audit export, access reviews, release/rollback) live in docs/soc2/.

Repository Notes

  • Package manager: pnpm
  • Main backend entrypoint: ts/index.ts
  • Frontend entrypoint: ts_web/index.ts
  • Browser SDK entrypoint: @idp.global/sdk/browser
  • CLI entrypoint: ts_idpcli/index.ts

This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the license.md file.

Please note: The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.

Trademarks

This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.

Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.

Company Information

Task Venture Capital GmbH
Registered at District Court Bremen HRB 35230 HB, Germany

For any legal inquiries or further information, please contact us via email at hello@task.vc.

By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.

S
Description
the code that runs the idp.global platform
Readme 4.7 MiB
Languages
TypeScript 97.3%
JavaScript 2.5%
Shell 0.1%