This release is a pre-release and may not be stable for production use.
polaris-id-cli
The operator command line for a Polaris ID database: the use-case stored procedures, inspection and read-only queries, and the operator records (users, authorities, quotas, revocation bounds, relying parties, authority keys, retention), without a browser. Polaris ID is pre-pilot software on notional data; the project README says what it is and what it is not.
Not wrapped as subcommands: close_anchor_batch (R10-2),
uc10_attest_trust / uc10_revoke_attestation (R11-3), uc11_close_epoch
(R10-1), uc12_record_duress (R11-5). Call them through the Flask API with
curl.
Install
pip install --pre polaris-id-cli # --pre: 1.0.0rc1 is a release candidate
pip install --pre 'polaris-id-cli[user-mgmt]' # adds werkzeug for user-create and user-passwd
polaris-id --help
Every command talks to a running Polaris PostgreSQL (below). Four also run through modules
of the Polaris application, which ship in the repository and not in this package: issue
and bulk-enroll sign through polaris_web/pqc_signing, migrate-population uses
polaris_web/migration, and transparency-report uses polaris_web/transparency. From a
package install those four refuse (exit 2) and say so. Run them from a clone:
git clone https://github.com/EgorKhaklin/polaris-id.git
cd polaris-id/polaris_cli
pip install -e '.[user-mgmt]'
polaris-id issue --help
Configuration
| Variable | Default |
|---|---|
POLARIS_DB_HOST |
localhost |
POLARIS_DB_NAME |
polaris_test |
POLARIS_DB_USER |
polaris_app |
POLARIS_DB_PASSWORD |
polaris_dev_password |
NO_COLOR |
unset (color enabled) |
Commands
| Command | Purpose |
|---|---|
| Inspect | |
health |
Row counts, token-status breakdown, PQ vs classical migration, disclosure distribution |
list <entity> |
individuals, agencies, tokens, algorithms, contexts, verifications; filters --status, --context, --outcome, --limit (default 50) |
inspect <token-id> |
Token record, lifecycle history, verification events, device bindings |
query "SQL" |
Read-only SELECT/WITH; 5,000-character limit, 5-second timeout, no DDL |
| Use cases | |
issue |
UC-1: create the Individual, provision, bind hardware, activate, grant contexts |
activate-reserve |
UC-4: lose an active token, promote a reserve |
bind-device |
UC-5: bind a device to an active token |
migrate-algorithm |
UC-6: migrate a token to a new algorithm |
warrant-audit |
UC-7: warrant-authorized verification history (ZK events excluded: no token_id is stored) |
revoke |
UC-8: revoke an ACTIVE token |
recovery-initiate |
UC-9 phase 1: open a catastrophic-loss recovery |
recovery-record-channel |
UC-9: record one out-of-band channel (BIOMETRIC, SWORN, WITNESS) |
recovery-complete |
UC-9 phase 2: approve or reject a pending recovery |
transition <token-id> <status> |
State-machine transition with an attributed audit row |
bulk-enroll <file> |
P2.4: stage a pipe-delimited extract with COPY and issue the batch set-based |
migrate-population |
P7.6: re-sign the whole ACTIVE population under a new algorithm, resumably |
transparency-report |
P7.7: the public transparency report for a period |
| Accounts | |
user-list |
Application users: role, active, last login, lockout (never hashes) |
user-create <username> <role> |
Create an application user |
user-passwd <username> |
Rotate a password; clears lockout |
user-deactivate <username> |
Soft-delete (is_active = FALSE); audit history kept |
user-history [username] |
Every recorded decision about an operator account |
audit-log |
Tail the authentication audit log |
| Authorities and policy | |
agency-create <name> |
Create an authority, with the reason it exists |
agency-history [agency_id] |
Every recorded decision about an authority |
quota-set <agency_id> / quota-show [agency_id] |
Per-agency caps; 0 clears a cap |
discretion-set <agency_id> / discretion-show [agency_id] |
Per-agency revocation-share bound over a rolling window |
rp-register <org_name> |
Register a relying party for the /api/v1 API |
rp-policy <client_id> |
Set a relying party's auth-broker policy |
rp-history [client_id] |
Every recorded decision about a relying party |
key-register / key-retire / key-compromise |
Authority signing-key lifecycle |
retention-show / retention-set |
Retention in force, and recording a decision |
polaris-id <command> --help lists every flag.
Inspect
polaris-id health
polaris-id list tokens --status ACTIVE
polaris-id list verifications --context BANKING --outcome SUCCESS --limit 20
polaris-id inspect 2
polaris-id query "SELECT context_type, COUNT(*) FROM VerificationEvent ve JOIN VerificationContext vc USING(context_id) GROUP BY context_type ORDER BY 2 DESC"
Use cases
polaris-id issue \
--legal-name "A. Holder" --dob 1990-01-15 --jurisdiction US-PA \
--agency 1 --algorithm 1 --token-value TKN-PA-2026-100 --serial SN-PA-100 \
--biometric IRIS --liveness MULTI_MODAL --witness 2 --hardware TitanQ-3 \
--contexts 1,4,6
polaris-id activate-reserve --lost-token 4 --reserve-token 7 --actor-agency 1 \
--reason LOST --crl-url "https://crl.idtoken.gov/2026/05/T4-LOST.crl"
polaris-id bind-device --token 2 --device-type PHONE \
--fingerprint "SE-AAPL-A19-newdevice12345" \
--binding-method SECURE_ENCLAVE --validity-months 24
polaris-id warrant-audit --individual 3 --context BANKING \
--window-start "2026-01-01 00:00:00" --window-end "2026-12-31 23:59:59"
polaris-id transition 2 DORMANT --actor 3 --reason QUARTERLY_REVIEW
polaris-id transition 4 LOST --actor 1 --reason HOLDER_REPORTED_THEFT
| Command | Flags |
|---|---|
issue |
--legal-name, --dob, --jurisdiction, --agency, --algorithm, --token-value, --serial, --contexts (required); --biometric (NONE/FINGERPRINT/FACE/IRIS, default IRIS), --liveness (PASSIVE/ACTIVE_CHALLENGE/MULTI_MODAL), --witness, --hardware. Signs through polaris_web/pqc_signing as the issuing route does (the agency's own key when it has one), and issues nothing it cannot sign |
activate-reserve |
--lost-token, --reserve-token, --actor-agency, --crl-url; --reason (LOST/STOLEN/COMPROMISED/SUPERSEDED/ADMINISTRATIVE) |
bind-device |
--token, --fingerprint; --device-type (PHONE/TABLET/WATCH), --binding-method (SECURE_ENCLAVE/TITAN_SECURITY/TRUSTED_PLATFORM_MODULE), --validity-months (default 12) |
migrate-algorithm |
--token, --new-algorithm, one of --signature-hex / --signature-file; --deprecate-old (one-way) |
warrant-audit |
--individual; --window-start, --window-end, --context |
revoke |
--token, --actor-agency, --reason (POLICY_VIOLATION/FRAUD_DETECTED/COMPROMISE/SUPERSEDED/ADMINISTRATIVE/INDIVIDUAL_REQUEST), --published-location; --cosigner-agency (R11-6) |
recovery-initiate |
--individual (must have no ACTIVE token), --requesting-agency, --requesting-user; --cooldown-hours (default 48) |
recovery-record-channel |
--recovery-id, --recording-user, --channel; --sworn-statement-hash (SWORN only, 64 hex) |
recovery-complete |
--recovery-id, --deciding-user, --decision (APPROVED/REJECTED), --reason; if approved: --new-token-value, --new-serial, --algorithm, --biometric-binding, --liveness-check, --published-location |
transition |
<new-status> in ACTIVE/DORMANT/REVOKED/LOST/EXPIRED; --actor, --reason |
bulk-enroll |
File columns legal_name|date_of_birth|jurisdiction|biometric_binding_type|token_value|physical_serial|permitted_contexts; --agency, --algorithm; --note, --dry-run. Signs each row as issue does; the whole batch rolls back on any rejection |
migrate-population |
--to (algorithm name, e.g. ML-DSA-87, or id); --batch (default 500), --limit, --dry-run, --deprecate-old (second pass, refused while any credential is unmigrated), --grace-seconds (default 1) |
transparency-report |
--period (e.g. 2026-Q3), --since; --published (repeatable), --anchor-target-hours, --checks-total, --checks-passed, --json |
Accounts
Usernames match [a-z0-9._-]{3,50} (lowercased automatically); roles are
admin, operator, auditor. Passwords are read interactively with
confirmation; --password is for scripting and shows in process listings.
Passwords need at least 12 characters with a digit, a letter and a symbol.
user-create requires --justification (at least 20 characters, recorded);
--actor names who created it.
An admin account gets a 30-day WebAuthn deadline, the same one
scripts/polaris-create-operator.sh --role admin sets
(docs/design/webauthn.md). During the 30 days a
password alone signs in and the interface asks for enrolment; after it, a
registered credential is required, and with none the login is refused and the
recovery path named. Operator and auditor accounts get no deadline.
polaris-id user-create alice operator --justification "Night-shift issuance operator for PA."
polaris-id user-passwd alice # also resets failed_login_count and locked_until
polaris-id user-deactivate alice # reactivate with a direct SQL update
polaris-id user-history alice --widened-only
audit-log shows logins, failures, lockouts, CSRF rejections, authorization
denials, rate-limit triggers, WebAuthn and session events. Rows are append-only
at the schema level.
polaris-id audit-log # last 50
polaris-id audit-log --event-type LOGIN_FAILED --since-minutes 60
polaris-id audit-log --username alice --limit 100
Authorities and policy
A --justification is stored with the decision; where one is required it must
be at least 20 characters.
| Command | Flags |
|---|---|
agency-create |
--agency-type (FEDERAL/STATE/COUNTY/PRIVATE/MUNICIPAL), --jurisdiction, --justification; --authorization-level (1-5, default 3), --actor |
agency-history |
--widened-only |
quota-set |
--issue-per-day, --revoke-per-day, --verify-per-hour, --justification; --set-by |
quota-show |
--history |
discretion-set |
--max-revoke-percent (system default 5.00), --justification; --window-days (1-365, default 30), --set-by |
discretion-show |
--history |
rp-register |
--rate-limit-per-min (default 120), --scope (verify/authenticate/both), --require-zk, --required-enrollment, --required-context, --justification |
rp-policy |
--require-zk / --no-require-zk, --required-enrollment (or none), --required-context (id or none), --justification (required when the change weakens the policy) |
rp-history |
--weakened-only |
key-register, key-retire, key-compromise |
<agency_id> <public_key_hex>; --effective-at (ISO-8601, default now; a compromise may predate discovery), --note, --algorithm (ML-DSA-65/ML-DSA-87, inferred from key length) |
retention-show |
--jurisdiction, --history |
retention-set |
--actor-user-id (an admin), --jurisdiction; either --template or --table-class (TOKEN_LIFECYCLE/VERIFICATION/ENROLLMENT/AUTH_AUDIT) with --days and --justification |
Retention is data in RetentionPolicy. STANDARD-5Y keeps every class five
years; MINIMIZED keeps the civic record five years and operational history
two. Three refusals: below 365 days (a CHECK constraint, so lowering the floor
is a schema change); editing or deleting a decision (decisions are superseded,
never changed); and a uc_archive_purge cutoff inside the window.
See docs/design/retention.md and
OPERATIONS.md.
polaris-id retention-show --jurisdiction=US-CA --history
polaris-id retention-set --actor-user-id=7 --jurisdiction=US-CA --template=MINIMIZED
polaris-id retention-set --actor-user-id=7 --jurisdiction=US-CA \
--table-class=AUTH_AUDIT --days=1095 \
--justification="State retention schedule 4.2 for operator access records."
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Usage / argument error / record not found / weak password |
| 2 | Database connection or generic SQL error |
| 3 | Procedure rejected the operation (constraint or business rule) |
| 130 | Interrupted (Ctrl-C) |
Testing
python3 test_cli.py
115 integration tests (v1.0.0-rc.62) cover every command, the user lifecycle (create, authenticate, rotate, deactivate), audit-log filtering and constraint violations. Each test resets the database to the sample state.
Expected output: 115 passed (v1.0.0-rc.62).
Scripting examples
# Bulk-issue from a CSV
while IFS=, read name dob jur token serial; do
polaris-id issue \
--legal-name "$name" --dob "$dob" --jurisdiction "$jur" \
--agency 1 --algorithm 1 --biometric IRIS \
--token-value "$token" --serial "$serial" \
--contexts 1,4
done < holders.csv
# Active tokens issued by one agency
polaris-id query "SELECT t.token_id, i.legal_name FROM IdentityToken t
JOIN Individual i ON t.individual_id = i.individual_id
WHERE t.issuing_agency_id = 1 AND t.status = 'ACTIVE'
ORDER BY t.token_id"
# JSON rows for jq
polaris-id query "SELECT row_to_json(t) FROM IdentityToken t WHERE token_id = 2"
# Failed logins per IP in the last hour
polaris-id query "SELECT ip_address, COUNT(*) FROM AuthAuditLog
WHERE event_type='LOGIN_FAILED'
AND event_timestamp > CURRENT_TIMESTAMP - INTERVAL '1 hour'
GROUP BY ip_address ORDER BY 2 DESC"
Metadata
Release files for polaris-id-cli 1.0.0rc1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| polaris_id_cli-1.0.0rc1.tar.gz | 71.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| polaris_id_cli-1.0.0rc1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 138.5 kB
Release files / polaris_id_cli-1.0.0rc1.tar.gz
| Download URL | polaris_id_cli-1.0.0rc1.tar.gz |
|---|---|
| Size | 71.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
edb75acf5e8e72fcf83e394c5016ec32aca61a8115407506b264f935226829b7
|
|
BLAKE2b-256 checksum How to use checksums |
778743590cf8376aed33f29d13bf63159b0370e10f53fc249e8bf15ec45e8e14
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency logRelease files / polaris_id_cli-1.0.0rc1-py3-none-any.whl
| Download URL | polaris_id_cli-1.0.0rc1-py3-none-any.whl |
|---|---|
| Size | 67.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a273785863fc2aaf375aec1a8923e50fe5a632148a1913b889a6b77576213a1e
|
|
BLAKE2b-256 checksum How to use checksums |
190db3a5528b1cd595777f34c4d6ed52bacea8d3c95096113de20bc8563ed49d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency log