
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
primitive-admin
Advanced tools
Command-line interface for administering Primitive applications. This CLI provides full control over your Primitive apps, including user management, integrations, prompts, workflows, and configuration sync.
npm install -g primitive-admin
From the worklet root directory:
# Install dependencies
pnpm install
# Build the CLI
pnpm run cli:build
# Link globally for system-wide access
npm link
Or from the cli directory directly:
cd cli
# Build
npx tsc
# Link globally
npm link
If you modify CLI source files, rebuild before testing:
# From cli directory
cd cli && npx tsc
# Or from worklet root
pnpm run cli:build
The globally linked primitive command uses the compiled files in cli/dist/, so changes to source files in cli/src/ won't take effect until you rebuild.
# Login via browser OAuth
primitive login
# Set your working app context
primitive use "My App"
# List users in the current app
primitive users list
# View help for any command
primitive --help
primitive apps --help
The CLI uses browser-based OAuth for authentication. When you run primitive login, it opens your browser to authenticate via Google OAuth and stores credentials locally.
# Login to the default server
primitive login
# Login to a custom server
primitive login --server https://api.example.com
# View current session
primitive whoami
# Get access token for scripting
primitive token
# Logout
primitive logout
Credentials are stored in ~/.primitive/credentials.json with mode 0600.
Most commands require an app ID. You can specify it in two ways:
primitive users list --app 01HXYZ...primitive use, then omit from commandsCommands that only need an app ID (like list) also accept it as a positional argument:
primitive users list 01HXYZ...
Commands with other required arguments use only --app for the app ID:
primitive workflows get <workflow-id> --app 01HXYZ...
# Interactive app selection
primitive use
# Set by name or ID
primitive use "My App"
primitive use 01HXYZ...
# View current context
primitive context
# Clear context
primitive context clear
Manage applications (requires admin access).
primitive apps list # List all accessible apps
primitive apps create "My App" # Create a new app
primitive apps get <app-id> # Get app details
primitive apps update <app-id> [opts] # Update app settings
primitive apps delete <app-id> # Delete an app
Update options:
--name <name> - App display name--mode <mode> - Access mode: public, invite-only, waitlist--base-url <url> - Application base URL--waitlist / --no-waitlist - Enable/disable waitlist--google-oauth / --no-google-oauth - Enable/disable Google OAuth--passkey / --no-passkey - Enable/disable passkey auth--magic-link / --no-magic-link - Enable/disable magic link authManage users within an app.
primitive users list [app-id] # List users
primitive users create <email> [--role admin|member] # Create/add user by email
primitive users invite [app-id] <email> [--role admin] # Invite user
primitive users remove [app-id] <user-id> # Remove user
primitive users set-role [app-id] <user-id> <role> # Change role
primitive users transfer-owner [app-id] <new-owner-id> # Transfer ownership
primitive users mint-jwt <user-id> [--role <role>] # Mint test JWT (dev/test only)
primitive users invitations [app-id] # List pending invitations
Manage waitlist entries for apps in waitlist mode.
primitive waitlist list [app-id] # List waitlist entries
primitive waitlist invite [app-id] <waitlist-id> # Invite from waitlist
primitive waitlist bulk-invite [app-id] -n 10 # Bulk invite
primitive waitlist remove [app-id] <waitlist-id> # Remove from waitlist
Manage HTTP integrations (external API connections).
primitive integrations list [app-id] # List integrations
primitive integrations create [app-id] [options] # Create integration
primitive integrations get <integration-id> # Get details
primitive integrations update <id> [options] # Update integration
primitive integrations delete <id> # Soft delete
primitive integrations test <id> # Test connection
primitive integrations logs <id> # View invocation logs
Create options:
--from-file <path> - Load from TOML file--key <key> - Integration key (identifier)--name <name> - Display name--base-url <url> - Base URL for API calls--timeout <ms> - Request timeout in millisecondsSecrets management:
primitive integrations secrets list <integration-id>
primitive integrations secrets add <id> --data '{"apiKey":"..."}'
primitive integrations secrets archive <id> <secret-id>
Manage encrypted app secrets (API keys, tokens, credentials). Values are encrypted at rest and never displayed after creation.
primitive secrets list [--app <app-id>] # List secrets (values never shown)
primitive secrets set <KEY> --value <value> [--summary <text>] # Create or update a secret
primitive secrets delete <KEY> # Delete a secret
Examples:
primitive secrets set OPENAI_API_KEY --value "sk-..." --summary "Production key"
primitive secrets set STRIPE_SECRET --value "sk_live_..."
primitive secrets list --json
primitive secrets delete STRIPE_SECRET
Keys must be uppercase letters, digits, and underscores (e.g., OPENAI_API_KEY). Max 100 secrets per app, 2 KB per value. The set command is an upsert — it creates or updates automatically. Use {{secrets.KEY}} in workflows and secrets.KEY in CEL rules.
Manage LLM prompt configurations.
primitive prompts list [app-id] # List prompts
primitive prompts create [app-id] [options] # Create prompt
primitive prompts get <prompt-id> # Get details
primitive prompts update <id> [options] # Update prompt
primitive prompts delete <id> # Soft delete
primitive prompts execute <id> --vars '{}' # Execute prompt
primitive prompts preview <id> --vars '{}' # Preview rendered template
Create options:
--from-file <path> - Load from TOML file--key <key> - Prompt key (identifier)--name <name> - Display name--provider <provider> - LLM provider (openrouter, gemini)--model <model> - Model name--system-prompt <text> - System prompt--user-template <text> - User prompt templateConfigs (prompt variations):
primitive prompts configs list <prompt-id>
primitive prompts configs create <prompt-id> [options]
primitive prompts configs activate <prompt-id> <config-id>
primitive prompts configs duplicate <prompt-id> <config-id>
Manage multi-step workflow definitions.
primitive workflows list [app-id] # List workflows
primitive workflows create [app-id] --from-file wf.toml # Create workflow
primitive workflows get <workflow-id> # Get details
primitive workflows update <id> [options] # Update metadata
primitive workflows draft update <id> --from-file wf.toml # Update draft
primitive workflows publish <id> # Publish draft
primitive workflows preview <id> --input '{}' # Preview execution
Run management:
primitive workflows runs list <workflow-id>
primitive workflows runs status <workflow-id> <run-id>
Manage long-lived API access tokens for headless/server authentication.
primitive tokens create <app-id> --name "My Token" --user <user-id> # Create token
primitive tokens create <app-id> --name "CI/CD" --ttl 90d --user <uid> # With expiry
primitive tokens list [app-id] # List tokens
primitive tokens show <token-id> [app-id] # Show details
primitive tokens revoke <token-id> [app-id] # Revoke token
Create options:
--name <name> - Token name (required)--user <user-id> - App user ID to associate the token with (required)--ttl <duration> - Token lifetime (e.g., 7d, 30d, 4w, 3m, 1y). Omit for never-expiringManage online databases and permissions. list returns databases the user has direct access to; group-shared databases are listed via primitive groups databases.
primitive databases list [app-id] # List databases (direct access)
primitive databases create <title> [app-id] # Create database
primitive databases get <database-id> [app-id] # Get details
primitive databases update <database-id> [options] # Update title or type
primitive databases delete <database-id> [app-id] # Delete database
Permissions:
primitive databases permissions list <database-id> [app-id]
primitive databases permissions grant <database-id> --user-id <uid> --permission <perm> [app-id]
primitive databases permissions revoke <database-id> <user-id> [app-id]
Permission values: owner (set at creation), manager
Metadata:
primitive databases metadata update <database-id> --data '{"key":"value"}' # Merge-update metadata
Operations:
primitive databases operations list <database-id> [app-id] # List registered operations
primitive databases operations execute <database-id> <op-name> --params '{}' # Execute operation
primitive databases operations execute <database-id> <op-name> --token <jwt> # Execute as specific user
The --token flag lets you execute an operation as a specific user using a test JWT from users mint-jwt. Useful for testing access rules.
Records (schema introspection):
primitive databases records models <database-id> [app-id] # List model names
primitive databases records describe <database-id> <model> [app-id] # Show inferred schema
Indexes:
primitive databases indexes list <database-id> [--model <name>] # List indexes
primitive databases indexes create <database-id> <model> <field> [options] # Create index
primitive databases indexes drop <database-id> <model> <field> # Drop index
Export / Import:
primitive databases export [app-id] <database-id> --output <dir> # Export records, indexes, constraints
primitive databases import [app-id] <path> --overwrite --dry-run # Import from export directory
Export creates a directory with metadata.json, records.jsonl, indexes.json, and constraints.json. Import restores records and indexes into a new or existing database. Database type config (operations, triggers, access rules) is managed separately via primitive sync — run sync push on the target app before importing.
Manage document ownership, group permissions, and export/import.
primitive documents transfer-owner [app-id] <document-id> <new-owner-id> # Transfer ownership
Group permissions on documents:
primitive documents group-permissions list <document-id>
primitive documents group-permissions grant <document-id> --group-type <type> --group-id <id> --permission <perm>
primitive documents group-permissions revoke <document-id> <group-type> <group-id>
Permission values: read-write, reader
Export / Import:
primitive documents export [app-id] <document-id> --output <dir> # Export Yjs state, blobs, permissions, aliases
primitive documents export-all [app-id] --user-id <id> --owned-only # Export all docs for a user
primitive documents import [app-id] <path> --overwrite --aliases overwrite|skip --dry-run # Import from export
Export creates a directory per document with metadata.json, document.yjs (Yjs state), permissions.json (for reference), and blobs/ (attachments). Permissions are exported for reference but not restored during import — the importing admin is the new owner and manages sharing in the target app. Document IDs are preserved across import. User-scoped aliases can be restored with --aliases overwrite (update existing) or --aliases skip (keep existing, default).
Manage groups, members, and memberships.
primitive groups list [app-id] # List groups
primitive groups create [app-id] --type <type> --id <id> --name <name> # Create group
primitive groups get <group-type> <group-id> [app-id] # Get details
primitive groups update <group-type> <group-id> [app-id] # Update group
primitive groups delete <group-type> <group-id> [app-id] # Delete group
Members:
primitive groups members list <group-type> <group-id> [app-id]
primitive groups members add <group-type> <group-id> <user-id> [app-id]
primitive groups members remove <group-type> <group-id> <user-id> [app-id]
primitive groups members set-role <group-type> <group-id> <user-id> <role> [app-id]
Memberships:
primitive groups memberships <user-id> [app-id] # List user's group memberships
Group resource access:
primitive groups documents <group-type> <group-id> # List documents a group can access
Group permissions on documents are managed via primitive documents group-permissions (see Documents above).
View usage analytics for an app.
# Overview & active users
primitive analytics overview [app-id] # DAU / WAU / MAU + growth
primitive analytics daily-active [app-id] # Daily active users time series
primitive analytics rolling-active [app-id] # Rolling active users (28 points)
primitive analytics cohort-retention [app-id] # Weekly cohort retention matrix
# Users
primitive analytics top-users [app-id] # Most active users
primitive analytics user-search [app-id] --query <q> # Search by email or ULID
primitive analytics user-detail <user-ulid> [app-id] # User activity breakdown
primitive analytics user-snapshot <user-ulid> [app-id] # Latest context snapshot
# Events
primitive analytics events [app-id] # Paginated event feed
primitive analytics events-grouped [app-id] # Events grouped by dimension
# Features
primitive analytics integrations [app-id] # Integration usage metrics
primitive analytics workflows [app-id] # Top workflows by runs
primitive analytics prompts [app-id] # Top prompts by executions
Common options:
--window-days <n> - Time window in days (default varies per command)--limit <n> - Result limit (top-users, workflows, prompts)--group-by <dim> - Dimension for events-grouped (action, feature, route, country, deviceType, plan, day)--page <n> - Page number for events feed (0-based)--json - Output raw JSONManage the admin directory.
primitive admins list # List all admins
primitive admins search <email> # Search by email
primitive admins invite <email> # Create invitation
primitive admins update <admin-id> [options] # Update settings
primitive admins disable <admin-id> # Disable account
primitive admins enable <admin-id> # Enable account
primitive admins invitations list # List invitations
primitive admins invitations delete <invitation-id>
Manage the global prompt and integration catalog.
# Prompt catalog
primitive catalog prompts list
primitive catalog prompts create --from-file prompt.toml
primitive catalog prompts get <catalog-id>
primitive catalog prompts update <catalog-id> [options]
primitive catalog prompts delete <catalog-id>
# Integration catalog
primitive catalog integrations list
primitive catalog integrations create --from-file integration.toml
primitive catalog integrations get <catalog-id>
primitive catalog integrations update <catalog-id> [options]
primitive catalog integrations delete <catalog-id>
Sync app configuration to/from TOML files for version control.
primitive sync init [app-id] --dir ./config # Initialize config directory
primitive sync pull [app-id] --dir ./config # Pull remote config to local
primitive sync push [app-id] --dir ./config # Push local config to remote
primitive sync diff [app-id] --dir ./config # Show differences
Options:
--dir <path> - Config directory (default: ./config)--dry-run - Show changes without applying (push only)--force - Overwrite remote changes (push only)The sync commands use TOML files for configuration. Here's the directory structure:
config/
.primitive-sync.json # Sync state (auto-generated)
app.toml # App settings
integrations/
weather-api.toml # Integration configs
prompts/
summarizer.toml # Prompt configs with all variations
workflows/
process-doc.toml # Workflow definitions
[app]
name = "My Application"
mode = "invite-only"
waitlistEnabled = false
baseUrl = "https://myapp.com"
[auth]
googleOAuthEnabled = true
passkeyEnabled = true
magicLinkEnabled = false
[cors]
mode = "custom"
allowedOrigins = ["https://myapp.com"]
allowCredentials = true
[integration]
key = "weather-api"
displayName = "Weather API"
description = "OpenWeatherMap integration"
status = "active"
timeoutMs = 30000
[requestConfig]
baseUrl = "https://api.openweathermap.org/data/2.5"
allowedMethods = ["GET"]
allowedPaths = ["/weather", "/forecast"]
[prompt]
key = "summarizer"
displayName = "Text Summarizer"
description = "Summarizes input text"
status = "active"
[[configs]]
name = "default"
provider = "openrouter"
model = "google/gemini-2.0-flash-001"
temperature = "0.7"
maxTokens = 500
systemPrompt = "You are a helpful assistant."
userPromptTemplate = "Summarize: {{ input.text }}"
[[configs]]
name = "concise"
provider = "openrouter"
model = "google/gemini-2.0-flash-001"
temperature = "0.3"
maxTokens = 200
systemPrompt = "You create very brief summaries."
userPromptTemplate = "Brief summary:\n{{ input.text }}"
All list/get commands support --json for machine-readable output:
# Human-readable table
primitive apps list
# JSON output for scripting
primitive apps list --json | jq '.[0].appId'
0 - Success1 - General error2 - Authentication error (try primitive login)PRIMITIVE_SERVER_URL - Server URL for login (defaults to https://primitiveapi.com)Server URL priority:
--server flag (highest priority)PRIMITIVE_SERVER_URL environment variablehttps://primitiveapi.com# Run without building (uses tsx)
pnpm run cli:dev -- login
# Type check
pnpm run cli:typecheck
# Build
pnpm run cli:build
The CLI includes both unit tests and integration tests.
Unit tests run without requiring a server connection:
# Run all unit tests
pnpm run cli:test:unit
Integration tests require a running server and valid admin credentials:
# Set required environment variables
export TEST_HTTP_URL=https://localhost:8787
export TEST_ADMIN_JWT=your-admin-jwt-token
export TEST_APP_ID=01HXYZ... # Optional: for app-scoped tests
# Run integration tests
pnpm run cli:test:integration
# Run all CLI tests
pnpm run cli:test
cli/tests/
unit/
config.test.ts # Credentials and config management
output.test.ts # Output formatting functions
integration/
api-client.test.ts # API client HTTP tests
commands.test.ts # CLI command tests
tokens.test.ts # Token lifecycle tests
databases.test.ts # Database CRUD, permissions, group permissions tests
documents.test.ts # Document group permissions, group resource listing tests
groups.test.ts # Group CRUD, members, memberships tests
classroom-e2e.test.ts # End-to-end classroom app workflow (types, rules, operations, access)
Run primitive login to authenticate.
Either pass an app ID as an argument, use --app <id>, or set context with primitive use.
The CLI automatically refreshes tokens. If issues persist, try primitive logout then primitive login.
Verify your server URL with primitive whoami and check network connectivity.
FAQs
CLI for administering Primitive applications
We found that primitive-admin demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.