@idp.global/devidp
@idp.global/devidp runs an exact local idp.global facade with fixed test identities and direct TLS. It consumes the canonical schema-v3 contract from @idp.global/interfaces/devidp, retains small stable secrets and a namespace CA in the Linux kernel keyring, starts immutable idp.global app and MongoDB images, and returns the relying-party credentials and TLS material needed by a local application.
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.
Runtime Requirements
- Linux x64
- Node.js 25
- a locally reachable Docker Engine and Docker CLI
- pnpm 11.18 or newer for development
- a usable persistent user kernel keyring
The package uses only SmartSecretKernelStore from @push.rocks/smartsecret. It never falls back to Secret Service, environment variables, or filesystem persistence. Unsupported or unavailable kernel-keyring environments fail closed.
Installation
pnpm add @idp.global/devidp
Usage
import { DevIdp } from '@idp.global/devidp';
const devIdp = new DevIdp({
namespace: 'freelance-local',
port: 8443,
rpClient: {
name: 'freelance.club local',
appUrl: 'https://localhost:3000/',
redirectUris: [
'https://localhost:3000/auth/callback',
'https://superadmin.localhost:3000/auth/callback',
],
postLogoutRedirectUris: [
'https://localhost:3000/',
'https://superadmin.localhost:3000/',
],
tlsHostnames: ['localhost', 'superadmin.localhost'],
},
});
const runtime = await devIdp.start();
console.log(runtime.origins.issuer);
console.log(runtime.client.clientId);
console.log(runtime.personas.testuser.email);
console.log(runtime.personas.testmoderator.email);
console.log(runtime.personas.testsuperadmin.email);
// Give runtime.caCertificatePem, runtime.rpTls, and runtime.client to the
// relying party, then retain devIdp for shutdown.
await devIdp.stop();
start() returns only after the app-owned result matches the exact manifest and all four direct HTTPS surfaces are ready: idp.localhost, login.idp.localhost, app.idp.localhost, and superadmin.idp.localhost. The app port is published only on 127.0.0.1. MongoDB has no published port and uses disposable tmpfs data directories.
The returned runtime contains the stable client id and secret, fresh relying-party TLS leaf, public local CA, and the email, password, subject, and exact authority for all three personas. stop() removes only the containers, networks, and temporary files captured by that lifecycle. Stable identity material remains in the kernel keyring for deterministic restart behavior.
withRuntimeMaterial() normalizes the complete manifest through the public schema-v3 owner before it touches durable or cryptographic state. Persona names and authority are therefore fixed:
testuser: ordinary user, no global capabilitiestestmoderator: non-admin with exactlyglobal.alert.manage,global.alert.read,global.user.manage, andglobal.user.readtestsuperadmin: global administrator with no direct capability assignments
Protected mutations continue to require the normal strong-MFA rules; the local profile does not create an MFA bypass.
For lower-level material inspection, deletion, explicit legacy migration, or callback-scoped access, use DevIdpFoundation. Application development should normally use the owning DevIdp lifecycle above.
Storage Contract
Every namespace is guarded by one cross-process namespace mutex and consists of one receipt plus nine deterministic payload accounts. Payloads bind their namespace digest, generation, purpose, kind, and value. The receipt binds the exact payload inventory plus every byte length and SHA-256 digest.
Creation writes and read-verifies every bounded payload before it writes the receipt. The receipt is the commit point and is written and read-verified last. Reads distinguish:
absent: receipt and all fixed payloads are absentincomplete: the receipt is absent but at least one fixed payload remainscommitted: the receipt and every bound payload validate exactly
A committed mismatch is corruption and fails closed. Incomplete uncommitted state can be cleaned and recommitted under the namespace mutex. Deletion invalidates the receipt first, removes every fixed payload, and absence-verifies the entire inventory.
The stable record contains independent 32-byte values for the confidential client, audit protector, TOTP encryption, OIDC transaction protection, and all three persona credentials. It also contains one namespace-bound P-256 CA and private key. Each kernel entry is at most 1,024 bytes.
Direct Local TLS
The stable CA is never silently rotated. Each withRuntimeMaterial() call creates independent fresh 30-day P-256 leaves:
- the IdP leaf contains exactly
idp.localhost,login.idp.localhost,app.idp.localhost, andsuperadmin.idp.localhost; - the relying-party leaf contains exactly the normalized
rpClient.tlsHostnamesinventory.
Issuance fails when the stable CA cannot contain the full leaf-validity window. The package never changes operating-system or browser trust stores; callers may explicitly retrieve the public CA certificate with readCaCertificatePem() and own any trust bootstrap themselves.
Legacy Keyutils Migration
migrateLegacyKeyutilsV1(namespace) is an explicit operator action. Stop the legacy writer first. The method reads only the narrow v1 keyutils source owned by SmartSecretKernelStore, strictly validates the entire legacy envelope, commits and reloads the v2 destination, and only then consumes the store-bound legacy receipt.
A keyutilsSourceAbsent result does not prove that Secret Service contains no old value. The package never probes or adopts Secret Service. Equivalent committed v2 and legacy values reconcile; conflicting or changed sources fail closed.
Resource and Secret Lifetime
Every operation attempts both kernel-store close and namespace-lease release, including failure paths. Simultaneous operation and cleanup failures are aggregated rather than swallowed. Callback-owned secret byte arrays are wiped afterwards on a best-effort basis, but JavaScript strings, garbage collectors, crypto libraries, and operating-system buffers may retain copies.
DevIdp starts two exact digest-bound images. MongoDB is attached only to an internal data network. The app is attached to that data network and to one dedicated bridge used for the loopback-only published TLS port. No Caddy or other local reverse proxy is started.
The public constants expose the immutable artifacts used by every lifecycle:
devIdpAppImage:code.foss.global/idp.global/app:13.1.0@sha256:cb2d1f44557d4846dcd31639bc18b379afc2a2dc473cf1defdd73380f05c87aedevIdpMongoImage:mongo:8.0.26-noble@sha256:b49841837cd7688885d7479d14a71733bacae4c99faaae615622384eaee045a0
The lifecycle passes secrets through distinct protected disposable files or mode-0600 Docker environment files, never through Docker command arguments. The app writes its result inside the container; the lifecycle copies and validates it before returning. Startup failure and shutdown both remove owned resources in dependency order and aggregate cleanup failures instead of hiding them.
If cleanup cannot remove an owned resource, its immutable id or unresolved exact-name/lifecycle recovery descriptor remains captured and the lifecycle remains in stopping. A later stop() retries recovery and those exact remaining ids; a new start() is refused until cleanup completes.
License and Legal Information
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the repository license 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.