Documentation
¶
Overview ¶
Package cmd holds the CLI subcommand implementations.
Index ¶
- Constants
- func Apply(args []string) error
- func Bootstrap(args []string) error
- func Check(args []string) error
- func Clearance(args []string) error
- func Dev(args []string) error
- func Doctor(version string, args []string) error
- func Domains(args []string) error
- func Env(args []string) error
- func Import(args []string) error
- func Init(_ []string) error
- func Keys(args []string) error
- func Listen(args []string) error
- func Login(args []string) error
- func Logs(args []string) error
- func MCP(args []string) error
- func Migrate(args []string) error
- func Orgs(args []string) error
- func Redirects(args []string) error
- func Users(args []string) error
- func UsersImport(args []string) error
- func Webhook(args []string) error
- func Webhooks(args []string) error
- func Whoami(args []string) error
- type Auth0User
- type CredEnvelopeRaw
- type Cursor
- type CursorSummary
- type IdentityRecord
- type ImportPlan
- type ImportRunner
- type LiveCredentials
- type LiveOptions
- type LivePuller
- type MembershipRecord
- type OrgRecord
- type Parser
- type PlanOptions
- type PlanParser
- type PlanRunner
- type PlanStats
- type RecordError
- type ScimDirectoryRecord
- type SourceUser
- type SsoConnectionRecord
- type UserRecord
Constants ¶
const TargetOrgExternalID = "authio:target-org"
TargetOrgExternalID is the sentinel org external_id used when target_organization_id pins imports to an existing Authio org.
Variables ¶
This section is empty.
Functions ¶
func Apply ¶
Apply builds the same plan as Check and executes it action by action, stopping at the first failure (already-applied actions stay applied — re-running apply is safe because every action is idempotent by identity key).
func Bootstrap ¶
Bootstrap dispatches `authio bootstrap <subcommand>`.
Today the only subcommand is `mint`, which calls the authio_management-api admin endpoint at POST /v1/admin/bootstrap-tokens to mint a single-use, hashed bootstrap token. The plaintext is printed to stdout exactly once; the CLI does NOT persist the plaintext to ~/.authio or anywhere else (that would defeat the single-use property).
Auth: the call carries a `sk_live_…` key as a Bearer token. The key must belong to the platform admin project (AUTHIO_ADMIN_PROJECT_ID on the management-api). Source order:
- --api-key flag
- AUTHIO_API_KEY env var
- ~/.authio/credentials.toml (`--profile`, default `default`)
The API URL follows the same order via --api-url / AUTHIO_MGMT_API_URL / the saved profile.
func Check ¶
Check loads authio.yaml, builds the plan, prints it, and exits 2 when there is drift — CI-friendly (0 = in sync, 1 = error, 2 = drift).
func Clearance ¶
Clearance is `authio clearance <login|serve|init|explain>` — the local sidecar for Authio Clearance (agent authorization). See internal/clearance.
func Dev ¶
Dev runs a local HTTP proxy that forwards to the configured auth-core (or --target=...) and pretty-prints every request/response in real time. Useful for SDK customers debugging integrations against the live alpha.
func Doctor ¶
Doctor diagnoses the local Authio setup and prints a pass/warn/fail checklist (or JSON with --json). `version` is the running CLI version, injected from main.
authio doctor [--profile name] [--json] [--repo owner/name] [--no-webhook-ping]
func Domains ¶
Domains handles `authio domains <subcommand>`.
The project comes from the secret key. There is no project id argument, so a key cannot be aimed at a different tenant.
func Env ¶
Env surfaces and switches the active Authio environment for CLI operations.
authio env [show] show the active profile's environment authio env list [--json] list configured profiles + their environment authio env use <profile> make a profile active for future commands
Design note (environments / A3): an Authio api key is environment- scoped — a `sk_test_` key only ever sees its non-production project's data, a `sk_live_` key only its production project's. There is no sk_-authed route to enumerate a tenant's *other* environments (that surface is `/v1/session/environments`, which requires a dashboard session, not an api key). So the CLI models "environments" as named credential profiles: each profile holds one environment-scoped key, and `env use` selects which one subsequent commands target. `env list` resolves each profile against `/v1/projects/me` to show its real environment + tenant.
func Import ¶
Import dispatches `authio import <provider> [flags]`.
Two flag dialects are supported in the same surface:
Legacy streaming (per-user POST + cursor; only auth0|clerk|cognito|firebase|supabase):
authio import <provider> --file <path> [--profile name] [--dry-run] [--force] [--rate-limit-rps N]
Plan-based (all 8 providers, what the migration wizard uses):
authio import <provider> --input <path>
--management-api-url <url> (or rely on --profile)
--api-key <key> (or rely on --profile)
[--orgs-table <file>] only for providers without native orgs
[--dry-run] prints the ImportPlan as JSON
[--json] stream NDJSON progress events (used by dashboard)
[--default-org-name N] name for the synthetic org for org-less providers
The two dialects are picked apart by which flag is present: `--input` (or `--plan`) triggers plan-mode. Plain `--file` keeps the legacy path.
func Keys ¶
Keys handles `authio keys <subcommand>`.
authio keys rotate [--profile name] [--name label]
Creates a replacement workspace secret key, updates credentials.toml, then revokes the previous key. Uses existing /v1/api-keys endpoints (create + delete) — the roll endpoint cannot rotate the key that is currently authenticating the request.
func Listen ¶
Listen forwards Authio events to a local HTTP endpoint — the Authio answer to `stripe listen`.
authio listen --forward http://localhost:3000/webhooks [flags]
Implementation (v1, zero server changes): the CLI POLLS the existing sk_-authed Events API (GET /v1/events) — the same cursor-paginated, project-scoped surface SDK consumers use — and replays each new event to the local target as a fully-formed Authio webhook: identical JSON envelope, an `Authio-Signature` HMAC computed with the exact scheme the webhooks worker uses, plus `Authio-Event-Id` / `Authio-Event-Action` headers. Because it polls, deliveries arrive with up to one poll interval of latency (default 2s) — fine for local development, not a production transport.
Signature passthrough: pass `--secret whsec_…` (a real endpoint's signing secret) to reproduce that endpoint's exact signature so your existing verification code runs unchanged locally. Omit it and the CLI generates a throwaway secret and prints it — set it in your local handler to verify.
func Logs ¶
Logs is `authio logs tail`. Phase 3.5 streams from authio_audit's query API; for now, we resolve the saved credentials and show how to query the audit log via curl. This is a stop-gap until the streaming endpoint lands.
func MCP ¶
MCP serves a newline-delimited JSON-RPC MCP server on stdio. Tools call the project-scoped secret-key API only.
func Migrate ¶
Migrate dispatches `authio migrate <subcommand>`.
The only public subcommand today is `migrate run --job-id <id>`, invoked by the authio_management-api when it queues a live-credentials import. The CLI:
- Asks the management-api for the job + the credential envelope (over HTTP, using AUTHIO_MIGRATE_WORKER_TOKEN as bearer).
- Decrypts the envelope using AUTHIO_IMPORT_CREDS_KEY.
- Runs the per-provider PullLive.
- Pipes the resulting plan through PlanRunner against the Authio management-API (using the same bearer).
- PATCHes progress / POSTs finish back to /v1/migrate/jobs/... so the dashboard's polling UI sees live updates.
`migrate plan --provider <p> --live-token <t> [--auth0-domain …]` bypasses the DB and prints the plan as JSON — useful for previews and for the e2e harness.
func Orgs ¶
Orgs handles `authio orgs <subcommand>`.
authio orgs create --name Acme [--slug acme] [--domain acme.com] [--profile name] [--json]
func Webhook ¶
Webhook handles `authio webhook listen <url>` — currently a placeholder that documents the recommended `ngrok http` workflow until we wire a first-party tunnel.
Types ¶
type CredEnvelopeRaw ¶
type CredEnvelopeRaw struct {
KekID string `json:"kek_id"`
Alg string `json:"alg"`
Nonce string `json:"nonce"`
Tag string `json:"tag"`
Ciphertext string `json:"ciphertext"`
DekNonce string `json:"dek_nonce"`
DekTag string `json:"dek_tag"`
DekCiphertext string `json:"dek_ciphertext"`
}
CredEnvelopeRaw mirrors the JSONB envelope the management-api stores. See authio_management-api/src/import_credentials.ts for the canonical shape; both sides MUST stay in lockstep.
func SealForTest ¶
func SealForTest(projectID, masterKey string, creds LiveCredentials) (CredEnvelopeRaw, error)
Exported test helpers — used by the e2e tests in authio_e2e-tests and the in-package tests in import_live_*_test.go.
SealForTest produces an envelope identical to what the management-api would write for `creds`. Tests use this to fake an import_credentials row without booting Postgres.
type Cursor ¶
type Cursor struct {
Provider string `json:"provider"`
File string `json:"file"`
FileSize int64 `json:"file_size"`
LastIndex int `json:"last_index"`
Completed bool `json:"completed"`
StartedAt time.Time `json:"started_at"`
LastUpdatedAt time.Time `json:"last_updated_at"`
Summary CursorSummary `json:"summary"`
}
Cursor is what gets written next to the input file as a resume marker. We key resume safety on (provider, file, fileSize); a size mismatch indicates the source file changed and we refuse to continue without --force.
type CursorSummary ¶
type CursorSummary struct {
Created int `json:"created"`
Existed int `json:"existed"`
Skipped int `json:"skipped"`
Errored int `json:"errored"`
}
CursorSummary tallies what the runner did across this import.
type IdentityRecord ¶
type ImportPlan ¶
type ImportPlan struct {
Provider string `json:"provider"`
Users []UserRecord `json:"users"`
Orgs []OrgRecord `json:"orgs"`
Memberships []MembershipRecord `json:"memberships"`
Identities []IdentityRecord `json:"identities"`
SsoConnections []SsoConnectionRecord `json:"sso_connections"`
ScimDirectories []ScimDirectoryRecord `json:"scim_directories"`
Warnings []string `json:"warnings"`
Stats PlanStats `json:"stats"`
}
ImportPlan is the canonical, provider-neutral shape that every plan parser emits. The plan-runner consumes it to drive idempotent writes against the Authio management-api.
Idempotency contract: every record carries an ExternalID of the form "<provider>:<source_id>". Re-running the importer with the same source export is safe — users are upserted on (project_id, email), orgs on (project_id, slug), memberships on (project_id, user_id, org_id), scim directories on (project_id, organization_id).
type ImportRunner ¶
type ImportRunner struct {
Parser Parser
File string
APIKey string
APIURL string
DryRun bool
Force bool
RateLimit int // requests per second; defaults to 50
HTTP *http.Client // injectable for tests
Out io.Writer // pretty-prints progress; defaults to os.Stdout
NowFunc func() time.Time
SleepFunc func(time.Duration)
UserAgent string
}
ImportRunner drives the standardized import flow: stream from a Parser, dedupe by cursor, rate-limit, POST /v1/users, count summaries.
type LiveCredentials ¶
type LiveCredentials struct {
// Auth0
Domain string `json:"domain,omitempty"`
Token string `json:"token,omitempty"`
// Clerk
SecretKey string `json:"secret_key,omitempty"`
// WorkOS
APIKey string `json:"api_key,omitempty"`
// Stytch (also reused by Descope for ProjectID)
ProjectID string `json:"project_id,omitempty"`
ProjectSecret string `json:"project_secret,omitempty"`
// Descope
MgmtKey string `json:"mgmt_key,omitempty"`
// Cognito
AccessKeyID string `json:"access_key_id,omitempty"`
SecretAccessKey string `json:"secret_access_key,omitempty"`
Region string `json:"region,omitempty"`
UserPoolID string `json:"user_pool_id,omitempty"`
// Firebase
ServiceAccountJSON string `json:"service_account_json,omitempty"`
// Supabase
PAT string `json:"pat,omitempty"`
ProjectRef string `json:"project_ref,omitempty"`
}
LiveCredentials is the union of every provider's admin-API credential shape. Most fields are nil for any single provider; the registered PullLive picks the ones it needs.
AUTHIO_REDACT — every field on this struct is a high-risk secret. Never log, never include in error messages verbatim, never write to stdout. The management-api ships these to the CLI via decrypted import_credentials.envelope and the CLI tosses them when the job terminates.
type LiveOptions ¶
type LiveOptions struct {
// BaseURLOverride lets tests redirect provider API calls at a
// localhost httptest.Server. Empty means use the real provider host.
BaseURLOverride string
// MaxPages caps pagination so a runaway export doesn't dominate a
// dev machine. 0 = unbounded.
MaxPages int
// RateLimitPerSec caps requests/sec. 0 falls back to providerDefault.
RateLimitPerSec float64
// HTTPClient lets the caller inject a custom transport (used by the
// migrate-run command for retries + telemetry).
HTTPClient *http.Client
// ProgressFn, if set, is called every batch with (kind, count). It
// powers the import_jobs.progress JSONB updates.
ProgressFn func(kind string, completed int)
// TargetOrganizationID pins memberships to an existing Authio org.
TargetOrganizationID string
// SourceWorkOSOrganizationID filters WorkOS live pulls to one org.
SourceWorkOSOrganizationID string
}
LiveOptions tweaks the puller's behavior (max page size, rate limit, base URL override for tests).
type LivePuller ¶
type LivePuller interface {
Name() string
PullLive(ctx context.Context, creds LiveCredentials, opts LiveOptions) (*ImportPlan, error)
}
LivePuller is what each provider implements.
func LivePullerFor ¶
func LivePullerFor(provider string) (LivePuller, error)
LivePullerFor returns the registered puller or a helpful error.
type MembershipRecord ¶
type Parser ¶
type Parser interface {
// Name is the provider key used in cursor + UA.
Name() string
// Help returns a short string describing the expected file format.
// Surfaced via `authio import <provider> --help`.
Help() string
// Parse reads from r and calls emit for every record. Records that
// should be skipped (disabled, banned, missing email, etc.) are
// emitted with Email == "" so the runner can count them as skipped
// without breaking the cursor's count-of-records-seen invariant.
Parse(ctx context.Context, r io.Reader, emit func(SourceUser) error) error
}
Parser streams users from a provider-specific export format. The runner passes a fresh `emit` callback each run; returning a non-nil error from emit (e.g. context cancellation) aborts parsing cleanly.
type PlanOptions ¶
type PlanOptions struct {
// OrgsTablePath, when non-empty, is a path to a JSON file describing
// an org graph for providers (Supabase, Firebase) that don't have a
// first-class org concept. Format:
//
// {
// "orgs":[{"external_id":"...", "name":"...", "slug":"...",
// "domain":"...", "members":[{"email":"...","role":"admin"}]}]
// }
OrgsTablePath string
// MergeDuplicateEmails enables WorkOS-style merge: source users with
// the same email across multiple source orgs collapse into one Authio
// user with N memberships. Default true.
MergeDuplicateEmails bool
// DefaultOrgName is used when a provider has zero orgs in its export
// (Supabase without --orgs-table, Firebase, Consumer Stytch). All
// users land under this org as members.
DefaultOrgName string
// TargetOrganizationID pins every imported membership (and SSO/SCIM)
// to this existing Authio org. WorkOS org rows are not created.
TargetOrganizationID string
// SourceWorkOSOrganizationID limits the import to one WorkOS org's
// users/memberships when set (optional).
SourceWorkOSOrganizationID string
}
PlanOptions modifies how a parser builds the plan.
type PlanParser ¶
type PlanParser interface {
Name() string
Help() string
ParsePlan(ctx context.Context, r io.Reader, opts PlanOptions) (*ImportPlan, error)
}
PlanParser is what each "production" importer implements. It reads the export file once and returns a fully-built ImportPlan.
Parsers do not hit the network and never see a project_id / API key — they only translate the source export. The PlanRunner does the writes.
func PlanParserFor ¶
func PlanParserFor(provider string) (PlanParser, error)
PlanParserFor returns the registered PlanParser for the given provider key, or nil + a helpful error.
type PlanRunner ¶
type PlanRunner struct {
APIURL string
APIKey string
HTTP *http.Client
Out io.Writer // progress sink; defaults to os.Stdout
EmitJSON bool // emit NDJSON events instead of human lines
DryRun bool
// ExtraHeaders are merged into every outbound request. Used by the
// migrate worker to send X-Authio-Worker + X-Authio-Project-Id so
// the management-api accepts the call without a real API key.
ExtraHeaders map[string]string
// TargetOrganizationID pins memberships to an existing Authio org.
TargetOrganizationID string
// RecordErrors collects per-record failures for the dashboard.
RecordErrors []RecordError
}
PlanRunner executes a parsed ImportPlan against the Authio management- API. It is idempotent on every record: users on (project_id, email), orgs on (project_id, slug), memberships on (project_id, user_id, org_id), scim directories on (project_id, organization_id).
The runner emits a structured per-record progress event when EmitJSON is true (used by the dashboard wizard). When false, it prints human- readable lines suitable for terminal use.
func (*PlanRunner) Run ¶
func (p *PlanRunner) Run(ctx context.Context, plan *ImportPlan) (PlanStats, error)
Run applies the plan and returns the final stats. The plan's Stats fields are updated in place.
type PlanStats ¶
type PlanStats struct {
SourceUsers int `json:"source_users"`
MergedUsers int `json:"merged_users"`
UsersCreated int `json:"users_created"`
UsersExisted int `json:"users_existed"`
OrgsCreated int `json:"orgs_created"`
OrgsExisted int `json:"orgs_existed"`
MembershipsCreated int `json:"memberships_created"`
IdentitiesCreated int `json:"identities_created"`
SsoConnectionsCreated int `json:"sso_connections_created"`
ScimDirectoriesCreated int `json:"scim_directories_created"`
Errored int `json:"errored"`
Warnings int `json:"warnings"`
}
type RecordError ¶
type RecordError struct {
Kind string `json:"kind"`
Key string `json:"key"`
Msg string `json:"msg"`
}
RecordError is one failed/skipped import row surfaced to the wizard.
type ScimDirectoryRecord ¶
type SourceUser ¶
type SourceUser struct {
Email string
Name string
EmailVerified bool
SourceID string
SourceProvider string
Metadata map[string]any
}
SourceUser is the canonical shape every parser emits. The runner doesn't care which provider it came from — it just calls POST /v1/users with (email, name, email_verified) and emits enrollment for created users.
type SsoConnectionRecord ¶
type UserRecord ¶
type UserRecord struct {
ExternalID string `json:"external_id"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
Name string `json:"name,omitempty"`
AvatarURL string `json:"avatar_url,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
MigrationPendingEmail bool `json:"migration_pending_email"`
MfaEnrolled bool `json:"mfa_enrolled,omitempty"`
// SourceExternalIDs is the list of every source-system ID that
// merged into this Authio user. For most providers it is a 1-element
// list. WorkOS produces N-element lists when the same email appears
// in multiple WorkOS orgs (the "marquee" merge moment).
SourceExternalIDs []string `json:"source_external_ids,omitempty"`
}
UserRecord — a single user, deduped by email within the plan.
Source Files
¶
- apply.go
- aws_sigv4.go
- bootstrap.go
- clearance.go
- client.go
- cmd.go
- dev.go
- doctor.go
- domains.go
- env.go
- import.go
- import_clerk_native.go
- import_live.go
- import_live_auth0.go
- import_live_clerk.go
- import_live_cognito.go
- import_live_descope.go
- import_live_firebase.go
- import_live_stytch.go
- import_live_supabase.go
- import_live_workos.go
- import_parsers.go
- import_plan.go
- import_plan_runner.go
- import_provider_auth0.go
- import_provider_clerk.go
- import_provider_cognito.go
- import_provider_descope.go
- import_provider_firebase.go
- import_provider_stytch.go
- import_provider_supabase.go
- import_provider_workos.go
- import_runner.go
- keys.go
- listen.go
- login.go
- mcp.go
- migrate.go
- orgs.go
- redirects.go
- users.go
- users_import.go
- webhooks.go
- whoami.go