Command reference
Six commands: scan, login, submit, logout, status, explain. Only login and submit ever touch the network. scan, status, and explain make zero network calls, no exceptions. Run with npx redential <command> (an alias of @redential/cli), or redential <command> after a global install.
A global --debug flag works on every command: verbose diagnostic logging to stderr (git commands, phase timings, counts), never the token or bundle content.
redential --debug scan --repo <path> redential scan --debug --repo <path> # either position works
scan
Scans the local git history and prints the proof bundle. Nothing is uploaded.
redential scan [options]
Flags
| Flag | Default | Description |
|---|---|---|
| --repo <path> | . | Path to the git repository to scan. |
| --author <email> | (none) | Author email that is yours. Repeatable. Passing at least one enables non-interactive mode for identity selection. |
| --yes | false | Confirm “I am authorized to analyze this repository” non-interactively. |
| --json | false | Force JSON-only output, even on an interactive terminal (this is already the default when stdout is piped). |
| --details | false | On a real terminal, add the COMMITS BY HOUR/WEEKDAY histogram sections to the summary. No effect with --json or piped output. |
| --since <spec> | (full history) | Limit analysis to commits at/after this date: a relative window ("2years", "18months", "30days") or an absolute date ("2024-01-01"). |
Interactively (a real TTY, no --author/--yes), scanasks which of the repo's author emails are yours, then asks you to confirm you're authorized to analyze the repository. Both are required before a bundle is produced. --author alone never implies authorization.
Output
What gets printed depends on whether stdout is a real terminal and on --json:
| stdout is... | --json | Output |
|---|---|---|
| piped or redirected | either | The exact bundle JSON only, byte for byte what submit would later send. No summary. |
| a real terminal (TTY) | no | The human-readable summary only, --details adds two extra sections. No JSON dump. |
| a real terminal (TTY) | yes | The exact bundle JSON only, nothing else, for scripting from an interactive shell. |
Run redential scan --json whenever you want the literal payload on screen, even in a terminal. On a TTY with no --json, the summary is a short, shareable overview rendered entirely from the same bundle scan already computed: structural findings first (tagged DIRECT/INFERRED, see Structural detection & explain), then everything else grouped by category, top languages and categories, ownership and signed-commit ratios, and a closing block restating what does (and never) leaves the machine:
PRIVATE WORK, LOCALLY DERIVED
1 year · 1,378 authored commits · 100% ownership
CAPABILITIES DETECTED
Payment webhook flow 30 commits STRUCTURAL · DIRECT
Frontend
Next.js 139 commits
React 113 commits
Tailwind CSS 80 commits
Zustand 40 commits
+1 more
Backend
Express 90 commits
Databases
PostgreSQL 60 commits
AI
Anthropic API 20 commits
TOP LANGUAGES
.ts ████████████████████ 62%
.tsx ██████░░░░░░░░░░░░░░ 20%
.md ███░░░░░░░░░░░░░░░░░ 8%
TOP CATEGORIES
Frontend ████████████████████ 59%
Backend ████████░░░░░░░░░░░░ 25%
Testing ███░░░░░░░░░░░░░░░░░ 10%
Ownership 100% of this repo's commits are yours
Signed commits 0% of your commits are cryptographically signed
Tip: signing future commits adds a stronger identity anchor to your attestation.
────────────────────────────────────────────────────────────
Nothing left your machine. Nothing is uploaded unless you run
`redential submit` — and only the bounded bundle: aggregates,
salted fingerprints, and closed-vocabulary capability slugs.
Never code, file names, commit messages, or other contributors.
Verify: github.com/Redential/redential-cli
────────────────────────────────────────────────────────────
Inspect the exact payload: redential scan --json
More detail (hour/weekday histograms): redential scan --detailsCapability groups are capped at 4 entries with an honest +N more, ordered by their own total commit count. Every field printed here, and every field in the raw JSON, is documented in Trust & privacy. Pipe it (redential scan | jq) and you get only the raw JSON: this summary is a terminal-only rendering of the same bundle, never a second source of data.
Warnings (non-blocking)
If the repo's remote looks like a known public host (github.com, gitlab.com, bitbucket.org), scanprints a warning to stderr suggesting the GitHub App instead. It never blocks or refuses to scan, because "known host" isn't the same as "publicly accessible" and scan has zero network access to tell the difference. The CLI's primary use case is a private employer repo hosted on github.com.
login
Authenticates via device flow (RFC 8628) and stores a session token locally. No flags.
redential login
Prints a verification URL and a short code, and makes a best-effort attempt to open the URL in your default browser. Auto-open is never load-bearing: on a headless box, over SSH, or if no browser is installed, it silently fails and login proceeds exactly as if auto-open didn't exist; the printed URL and code are always the fallback. Approve the code in the browser, and login polls until it receives an access token, then stores it at ~/.config/redential/credentials.json (mode 0600).
REDENTIAL_SITE_URL, submit refuses and asks you to log in again rather than silently sending a token to a different host. See Troubleshooting.submit
Scans, shows you the bundle, and uploads it after you confirm. Requires a prior redential login.
redential submit [options]
Flags
| Flag | Default | Description |
|---|---|---|
| --repo <path> | . | Path to the git repository to scan, same as scan. |
| --author <email> | (none) | Author email that is yours. Repeatable. Enables non-interactive identity selection, same as scan. |
| --yes | false | Confirm “I am authorized to analyze this repository” non-interactively, same meaning as scan --yes. |
| --confirm-upload | false | Confirm the upload itself non-interactively. Separate from --yes on purpose: consenting to be scanned and consenting to upload are different decisions. |
| --label <text> | (none) | A private nickname for this repo, visible only to you. Required when not running in an interactive terminal. |
redential submit --repo . --author you@company.com --yes --confirm-upload --label "acme backend"
--label is mandatory on every submit. On a real terminal without it, submit prompts interactively; without a terminal (a script, CI) and no --label, submit refuses immediately, before any network call at all. See Private label for what it is and why it's required.What happens, in order
| Step | Behavior |
|---|---|
| 1. Build the bundle | Exact same code path as scan, same author selection, same authorization prompt. |
| 2. Resolve the private label | --label <text> if given, validated immediately. Required, non-interactively, before any network call: on a real terminal without it, the actual prompt fires later, inside step 3. |
| 3. Show what gets uploaded | A short summary, an identity-corroboration line (if it succeeds), a “WHAT GETS UPLOADED” consent box, THEN the interactive label prompt (only if --label wasn't given), then the exact bundle JSON and the label line, all before the upload prompt. |
| 4. Ask to upload | “Upload this bundle?”, a separate confirmation from step 1's authorization attestation. |
| 5. Visibility gate | An anonymous HEAD request straight to the repo's remote URL (never to Redential's servers). A confirmed public remote (2xx/3xx) blocks the upload outright. |
| 6. Upload the bundle | POST to Redential with the bearer token from login. On success, only the bundle id is printed, never the full response. |
| 7. Upload the private label | A second POST, only after step 6 succeeds. A failure here never retries and never re-uploads the bundle: it warns and submit still exits 0. |
The visibility gate is submit-only. It's a real network check, stronger than scan's local heuristic. Anything inconclusive (private/gated, network error, timeout) doesn't block; it falls back to the same warning scan would have shown, and proceeds.
logout
Deletes the locally stored session token. No flags.
redential logout
Removes ~/.config/redential/credentials.json only. It never touches the device salt (a separate file used to make repo/author fingerprints non-reversible), which is device-local and unrelated to your session.
status
A read-only snapshot of local CLI state: login state, config dir, and the last submission on record. Zero network, works whether or not you're logged in. No flags.
redential status
redential 0.5.0 Config dir: ~/.config/redential Site: https://www.redential.com Logged in: yes (https://www.redential.com) Last submission: 2026-07-14T18:02:11.000Z to https://www.redential.com bundle hash: 7be2f1a9c3d4… repo fingerprint: a3f9e21b0c88…
Reads only files this CLI itself already writes (credentials.json, last-submission.json): never the scanned repo, never a git command, never the network. It never prints access_token. The bundle hash and repo fingerprint shown are 12-character prefixes only, enough to eyeball "is this the record I think it is" without printing the full value into a terminal that might get pasted into a support thread.
explain
Local-only: explains the structural detection for one taxonomy skill slug, why it was (or wasn't) detected, and whether it's claimed. Zero network, no output written anywhere, no --json mode. See Structural detection & explain for what "structural" means.
redential explain <skill> [options]
Flags
| Flag | Default | Description |
|---|---|---|
| --repo <path> | . | Path to the git repository to inspect. |
| --author <email> | git config user.email | Author email to attribute against. Repeatable. Defaults to the repo's own git config user.email. |
| --since <spec> | (full history) | Limit attribution to commits at/after this date, a relative window ("2years", "18months", "30days") or an absolute date ("2024-01-01"), same as scan --since. |
Unlike scan's prompted author picker, author selection here is non-interactive on purpose: explainnever builds or sends anything, so it's meant to run unattended, by a script or a developer piping it around. The output below is illustrative, built from the CLI's real print templates with made-up file paths and line numbers, not a captured run:
Skill: payments/payment-webhook-flow — Payment webhook flow
Classification: DIRECT — all three anchors sit in the same function or the same file — the strongest, most direct signal.
Anchors:
webhook-verification:
src/webhooks/stripe.ts:14 in handleStripeWebhook — stripe.webhooks.constructEvent (receiver resolved to import "stripe")
db-write:
src/webhooks/stripe.ts:22 in handleStripeWebhook — prisma .upsert(...) (receiver resolved to import "prisma")
idempotency-guard:
src/webhooks/stripe.ts:22 in handleStripeWebhook — upsert-shaped write is idempotent by construction (prisma .upsert(...) (receiver resolved to import "prisma"))
Connection: src/webhooks/stripe.ts (same-function — all anchors inside one function, 0 file hops)
Attribution (author: you@company.com):
YES — your own added-lines diff touched: src/webhooks/stripe.ts
Claimed: yesexplain is still a narrow spike, not a general detector. It can currently only explain the six structural patterns the CLI ships: the Stripe, PayPal, Mercado Pago, Lemon Squeezy, and Paddle webhook flows, plus the IAP/RevenueCat purchase flow. An ordinary import-tier slug, like payments/stripe, gets a plain error naming the slugs explain can currently cover, instead. An unknown slug (not in taxonomy.json at all) is a usage error too. Both exit 1.
An AMBIGUOUSclassification (pattern present but not fully connected, or missing an anchor kind entirely) is never claimed, and never appears in a bundle: it's local, on-screen feedback only, here specifically to show you why a skill you expected to see wasn't detected. A repository with no structural finding at all for the requested skill prints a plain "not detected" message and exits 1.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success. |
| 1 | Any handled failure: a scan/auth/submit/network error, a denied or expired device-flow login, or EOF on an interactive prompt (see Troubleshooting). |
Every command-level error is one of four typed errors (scan, auth, submit, or network), and every one of them sets a non-zero exit code rather than hanging or silently succeeding. Network-error messages are built only from the request's host and HTTP status, never from headers or body, so a failed request can never echo your bearer token or bundle content into a printed error.
Something not matching what you see locally? Open an issue.
← CLI overview