CLI

The oidc.pub CLI is a single binary for everything you do from the command line:

  • Authenticate and manage services on oidc.pub
  • Sync openid-configuration and JWKS from your own issuer
  • Run a local OIDC issuer for development and testing

It also serves as the sync worker for Docker and Kubernetes deployments — see the Sync Worker documentation.

Installation

The CLI requires Node.js 22+ or Docker.

npx oidc.pub --help

Anonymous testing and claims debugging

Anonymous services are built for short-lived OIDC testing, relying-party setup, and claim matching/debugging before you create an account or wire up a real issuer. They get a random -anon subdomain, expire after at most 24 hours, and are managed by a one-time secret returned when the service is created.

If you already have an issuer to publish, create and sync an anonymous service in one command:

npx oidc.pub service sync -a --source-url https://issuer.internal --once

This command creates a temporary oidc.pub service, prints its one-time management secret, publishes the issuer's discovery document and JWKS to the anonymous subdomain, and leaves the service available until you delete it or it expires.

For local claims debugging, use the built-in dev issuer's anonymous mode instead:

npx oidc.pub dev issuer --anonymous

The dev issuer mode creates the anonymous service before choosing its issuer URL, so the JWTs it mints have an iss claim that matches the public -anon discovery endpoint.

Use the local token endpoint to inspect or tune claims:

curl -s -X POST http://localhost:9229/token \
  -H "Content-Type: application/json" \
  -d '{"sub": "ci-bot", "aud": "my-api", "repository": "acme/app", "ref": "refs/heads/main"}' | jq .

Then point the relying party at the anonymous issuer URL shown by the CLI and adjust its claim rules until the token is accepted. Delete the service from the anonymous dashboard when the test is done, or let it expire automatically.

Try it locally with dev issuer

You don't need a real OIDC issuer to try oidc.pub. The CLI ships with a built-in dev issuer that generates an RSA keypair, serves the discovery endpoints locally, and mints signed JWTs — all auto-synced to your oidc.pub service on startup.

1. Create a service

npx oidc.pub login
npx oidc.pub service create --title "Demo"
# • Service created: Demo
#   Subdomain: demo-7xk2

For a disposable, no-login trial, create an anonymous service instead:

npx oidc.pub service create --anonymous --title "Demo"
# • Anonymous service created: Demo
#   Subdomain: q8m4p2za-anon
#   Expires: 2026-06-10T18:00:00.000Z

The command prints the anonymous management secret to stdout. Store it immediately; it is not shown again.

2. Run the dev issuer

Pass --service to derive the issuer URL and sync the discovery document on startup:

npx oidc.pub dev issuer --service demo-7xk2
# • Local issuer running on http://localhost:9229
# • Synced openid-configuration and JWKS to https://demo-7xk2.oidc.pub

Leave it running. The issuer URL embedded in tokens is https://demo-7xk2.oidc.pub, so any verifier that trusts that issuer can validate the tokens you mint.

You can also let the dev issuer create and clean up an anonymous service automatically:

npx oidc.pub dev issuer --anonymous
# • Anonymous service created: Anonymous dev issuer 1a2b3c4d
# • Local issuer running on http://localhost:9229
# • Synced openid-configuration and JWKS to https://q8m4p2za-anon.oidc.pub

3. Mint a token

In a second terminal, request a signed JWT:

TOKEN=$(curl -s http://localhost:9229/token | jq -r .access_token)

Or mint a token with custom claims:

TOKEN=$(curl -s -X POST http://localhost:9229/token \
  -H "Content-Type: application/json" \
  -d '{"sub": "ci-bot", "aud": "my-api"}' | jq -r .access_token)

4. Verify against the public JWKS

The discovery document and JWKS are now served by oidc.pub. Inspect them with curl:

export ISSUER_URL=https://demo-7xk2.oidc.pub
curl -s "$ISSUER_URL/.well-known/openid-configuration" | jq .
curl -s "$ISSUER_URL/.well-known/jwks.json" | jq .

Then verify the token signature using the public JWKS:

npm install jose
node --input-type=module --eval "
import { jwtVerify, createRemoteJWKSet } from 'jose';
const issuer = process.env.ISSUER_URL;
const jwks = createRemoteJWKSet(new URL(issuer + '/.well-known/jwks.json'));
const { payload } = await jwtVerify(process.argv[1], jwks);
console.log('Token is valid. Claims:', payload);
" "$TOKEN"

Any OIDC-aware relying party — Vault, GitHub Actions, AWS STS, GCP Workload Identity Federation — configured to trust the issuer URL shown by the CLI will now accept these tokens.

Sync from your own issuer

Once you have a real OIDC issuer (Vault, GitLab, a Kubernetes API server, your own service), point oidc.pub at it with service sync:

npx oidc.pub service sync \
  --service demo-7xk2 \
  --source-url https://your-issuer.internal \
  --once

Drop --once to keep syncing on an interval. For long-running deployments inside Docker or Kubernetes, use the same binary as a container — see the Sync Worker documentation.

Authentication

login

Authenticate via browser-based OAuth. Opens your browser, completes the login flow, and stores a session token locally.

FlagDescriptionDefault
--oidcpub-url <url>oidc.pub base URLhttps://oidc.pub
--profile <name>Credential profiledefault
--provider <name>OAuth providergithub

whoami

Display the currently logged-in user and session status.

logout

Remove stored credentials for the current profile.

Services

service create

Create a new service on oidc.pub. Requires authentication via login or the OIDCPUB_API_KEY environment variable.

FlagDescriptionRequired
--title <title>Human-readable service titleyes, except with --anonymous
--service-name <name>Custom service URL name prefix (Business and Enterprise only)no
--description <desc>Service descriptionno
-a, --anonymousCreate a temporary anonymous service without logging inno
--ttl-hours <hours>Anonymous service lifetime, up to 24 hoursno

service sync

Sync OIDC discovery configuration from a source issuer to oidc.pub. Same engine as the sync worker container.

FlagDescriptionDefault
--service <subdomain>Target service subdomain (required unless using --anonymous)
--service-title <title>Look up the target service by title instead of passing --service
--source-url <url>OIDC issuer URL to sync from
-a, --anonymousCreate a temporary anonymous service and sync to it without logging infalse
--ttl-hours <hours>Anonymous service lifetime, up to 24 hours24
--api-key <key>API key or JWT (alternative to login)
--onceSync once and exitfalse
--interval <seconds>Seconds between syncs300

Service accounts

Service accounts provide programmatic access to manage your services. Commands are available under service-account (aliased to sa). Available on the Team plan and above.

service-account create

Create a service account. A static policy returns a one-time bearer token (printed to stdout); store it immediately, it is never shown again.

FlagDescriptionDefault
--name <name>Service account name (required)
--services <list>Comma-separated subdomains the account may act on. Use * for all.*
--ip-allowlist <list>Comma-separated IPs/CIDRs to restrict the token
--policy-file <path>JSON policy document; overrides --services for full control (e.g. OIDC policies)
npx oidc.pub service-account create --name "CI/CD Pipeline" --services "*"

The policy file is validated against https://oidc.pub/schemas/service-account-policy.v1.json. Reference the URL via $schema for editor autocomplete.

service-account list

List your service accounts.

service-account delete <id>

Permanently delete a service account. Any token it issued stops working immediately.

service-account regenerate-token <id>

Regenerate the token for a static service account. The previous token is invalidated atomically; the new token is printed to stdout.

Local development

dev issuer

Run a local OIDC issuer with an in-memory RSA keypair. Serves discovery and JWKS endpoints and mints signed JWTs. With --service, the discovery document and JWKS are pushed to your oidc.pub service on startup.

FlagDescriptionDefault
--service <subdomain>Target oidc.pub service to sync to
--service-title <title>Look up the target service by title instead of passing --service
--url <url>Issuer URL embedded in tokens and discoveryderived from --service
-a, --anonymousCreate a temporary anonymous service, sync to it, and delete it on exitfalse
--ttl-hours <hours>Anonymous service lifetime, up to 24 hours24
--port <port>Local HTTP port9229
--subject <sub>Default sub claimtest-user
--audience <aud>Default aud claim
--claims <json>Extra claims as a JSON object

Endpoints served by the local issuer:

PathPurpose
GET /.well-known/openid-configurationOIDC discovery document
GET /.well-known/jwks.jsonPublic JWKS
GET /tokenMint a JWT with default claims
POST /tokenMint a JWT with claims from the JSON body

Profiles

Switch between accounts or environments with --profile. Each profile has its own session token and base URL.

npx oidc.pub login --profile staging --oidcpub-url https://staging.oidc.pub
npx oidc.pub service create --profile staging --title "Test Service"

The default profile is default.

Credential storage

Credentials are stored in ~/.config/oidcpub/credentials.json (respects XDG_CONFIG_HOME). The file is created with mode 0600. Session tokens expire after 36 hours.

Legacy Docker sync mode

The latest image is backward compatible with existing sync worker deployments. When OIDCPUB_SERVICE_ID is set and no subcommand is given, the container runs in legacy sync mode automatically. Existing Docker and Kubernetes deployments continue to work without changes after updating the image reference.