Complete reference for Sigcli — the authentication CLI that signs requests on your behalf. Log in once, use credentials everywhere.
Sigcli (sig) is a personal seal of authority. It handles browser SSO, stores tokens, and injects credentials into any command — so you log in once and every tool just works.
1234npm install -g @sigcli/cli # or without global install: npx @sigcli/cli sig --help
Run sig init to create ~/.sig/config.yaml, then sign in to your provider. Sign in with a real browser — credentials are captured automatically via browser SSO.
12345678# 1. Create config (interactive) sig init # 2. Sign in — opens a real browser, captures cookies automatically sig login https://jira.example.com # 3. Confirm it worked sig status my-jira
sig run is the recommended way to use credentials. It injects SIG_<PROVIDER>_* environment variables directly into the child process — nothing leaks into shell history or process lists.
12345678# Discover what variables are available sig run my-jira -- env | grep SIG_ # Run any command with credentials injected sig run my-jira -- curl https://jira.example.com/api/me # Run a Python script sig run my-jira -- python fetch_issues.py
The proxy is the most secure way to use credentials. It runs a local MITM daemon that intercepts HTTPS traffic and injects credentials transparently — your tools never see the tokens.
123456789101112131415# Start the proxy daemon sig proxy start # Trust the CA certificate (first time only) sig proxy trust # prints the CA cert path — add it to your OS trust store # Set the proxy env vars export HTTP_PROXY=http://127.0.0.1:7891 export HTTPS_PROXY=http://127.0.0.1:7891 # Now any HTTP client automatically gets credentials injected curl https://jira.example.com/rest/api/2/myself # When done sig proxy stop
For one-off API calls, sig request makes an authenticated HTTP request directly. Credentials stay inside the CLI process and are never exposed to subprocesses or shell history.
12345678# Simple GET request sig request https://jira.example.com/rest/api/2/myself # POST with body sig request https://api.example.com/data --method POST --body '{"key": "value"}' # Just the response body sig request https://api.example.com/me --format body
Pick the method that matches your use case. When in doubt, prefer higher security.
| Use Case | Recommended | Why |
|---|---|---|
| Long-lived daemon or AI agent | sig proxy | Credentials never leave proxy process |
| One-off API call or script | sig request | Credentials in-process only, never on disk/env |
| Wrapping a tool that reads env vars | sig run | Injects SIG_* env vars, redacts output |
| Debugging credential values | sig get | Prints to stdout — use with caution |
Credentials are stored in ~/.sig/credentials/ as sealed JSON files. Nothing goes into your repo, shell history, or environment by default.
Sigcli offers four ways to access credentials. Each makes a different tradeoff between security and convenience. Listed from most to least secure:
| Method | Credential Exposure | Lifetime | Visible To | Security |
|---|---|---|---|---|
| sig proxy | Never leaves proxy process | Proxy daemon lifetime | Nothing external | ●●●●● Highest |
| sig request | In-process memory only | ~100ms per request | Nothing external | ●●●●○ High |
| sig run | Child process env vars | Child process lifetime | /proc/PID/environ, child processes | ●●●○○ Moderate |
| sig get | Printed to stdout | Captured by shell | Terminal, shell history, pipes | ●●○○○ Low |
The MITM proxy daemon runs on localhost (127.0.0.1). Client apps set HTTP_PROXY and make normal requests. The proxy intercepts HTTPS connections, injects credentials as HTTP headers, and forwards to the upstream server.
Credentials are decrypted from storage and held only in the proxy process memory. Client applications, subprocesses, and env vars never contain tokens. TLS interception uses ECDSA P-256 certificates generated per hostname.
1sig proxy start && export HTTP_PROXY=http://127.0.0.1:7891 HTTPS_PROXY=http://127.0.0.1:7891
Use when: AI agents, CI/CD pipelines, long-running daemons, any tool that supports HTTP_PROXY.
sig request loads credentials into process memory, makes a single HTTP request, and discards them. Credentials are never written to env vars, files, or stdout. The exposure window is ~100ms per request.
1sig request https://api.example.com/me --format body
Use when: One-off API calls, shell scripts, pipeline steps that need a single HTTP response.
sig run injects SIG_<PROVIDER>_* env vars into the child process. This is convenient for tools that read configuration from environment variables, but env vars are readable via /proc on Linux and inherited by all child processes. Output from the child is redacted (credential values replaced with ****) but redaction is best-effort.
1sig run my-jira -- bash -c 'curl -H "Cookie: $SIG_MY_JIRA_COOKIE" https://jira.example.com/api/me'
Use when: Wrapping tools that read SIG_* env vars, local development, quick scripting.
sig get outputs credential headers to stdout. By default, values are redacted (****), but --no-redaction reveals raw tokens. Even redacted output shows credential structure. Raw values are visible in terminal scrollback, shell history (~/.bash_history, ~/.zsh_history), and piped commands.
1234# Redacted by default sig get my-jira # Raw values (use with caution) sig get my-jira --no-redaction
Use when: Debugging credential format, manual API testing. Never pipe raw output into AI agent context or logs.
All four methods share these protections:
~/.sig/credentials/ are encrypted. The encryption key lives at ~/.sig/encryption.key (mode 0o400, owner-read only).~/.sig/audit.log as JSON Lines.Result<T, AuthError>), never throw exceptions. Credentials are never exposed in error messages.All commands accept --verbose for debug output on stderr and --help to show usage.
Creates ~/.sig/config.yaml interactively. On headless machines use --remote to enable browserless mode.
12345sig init # interactive setup sig init --remote # headless / CI / remote machine sig init --yes # accept all defaults, skip prompts sig init --force # overwrite existing config sig init --channel chrome # use a specific browser (chrome|msedge|chromium)
Checks Node version, Playwright installation, config parsing, and that the credentials directory is writable. Run this first when something doesn't work.
1sig doctor
The recommended way to use credentials. Runs any command with SIG_<PROVIDER>_* environment variables injected. Credential values are automatically redacted from the child's stdout and stderr.
123456789101112131415161718192021sig run [provider...] -- <cmd> # Discover available SIG_<PROVIDER>_* variables sig run my-jira -- env | grep SIG_MY_JIRA_ # Run with credentials injected sig run my-jira -- python fetch_issues.py sig run my-jira -- node export_board.js # Multiple providers at once sig run provider-a provider-b -- python cross_tool.py # No providers — inject all valid credentials sig run -- python script.py # Expand individual cookies as SIG_<PROVIDER>_COOKIE_<NAME>=value sig run my-jira --expand-cookies -- python script.py # Write credentials to a .env file (auto-deleted after child exits) sig run my-jira --mount .env -- node app.js sig run my-jira --mount creds.json --mount-format json -- node app.js
Why sig run over sig get? sig get exposes raw tokens in shell variables, ps output, and AI agent context. sig run injects credentials directly into the child environment and redacts them from output — nothing leaks.
Authenticates with a provider. Accepts a URL or provider ID. By default uses a 3-phase cascade: headless Playwright → native CDP (real browser, no automation markers) → visible Playwright. Use --mode to force a specific mode.
12345678910111213141516171819202122232425262728293031sig login <url> # Browser SSO (opens browser automatically) sig login https://jira.example.com # Force native CDP mode (bypasses anti-bot detection on X, Reddit, etc.) sig login https://x.com --mode cdp # Force headless only (fastest, but blocked by some sites) sig login https://jira.example.com --mode headless # Custom provider ID sig login https://jira.example.com --as my-jira # API token / Personal Access Token (no browser) sig login https://jira.example.com --token <your-pat> # Cookies copied from browser DevTools → Network → Copy as cURL sig login https://jira.example.com --cookie "SESSION=abc123; csrf_token=xyz" # HTTP Basic auth sig login https://jira.example.com --username alice --password hunter2 # Force a specific strategy sig login https://jira.example.com --strategy cookie sig login https://jira.example.com --strategy oauth2 sig login https://jira.example.com --strategy api-token sig login https://jira.example.com --strategy basic # Skip stored credential check, go straight to browser sig login https://jira.example.com --force
12sig logout my-jira # clear one provider sig logout # clear all credentials
Retrieves credential headers for a provider. Prefer sig run or sig request over sig get — the latter exposes raw values in your shell.
12345sig get my-jira # JSON map (default) sig get my-jira --format json # structured JSON sig get my-jira --format header # HTTP header string sig get my-jira --format value # raw value only sig get jira.example.com # by URL
Strategy selection: use --token, --cookie, or --username/--password when you already have credentials — no browser needed. Only launch the browser for SSO sites.
Makes an authenticated HTTP request. Credentials stay internal — never appear in shell history.
12345678sig request <url> sig request https://jira.example.com/api/me sig request https://jira.example.com/api/issues/123 --format body sig request https://jira.example.com/api/issues --method POST --body '{"title":"Bug","status":"open"}' --header "Content-Type: application/json" sig request <url> --format json # full response (status, headers, body) sig request <url> --format body # body only sig request <url> --format headers # response headers only
123sig status # all providers sig status my-jira # one provider sig status --format json # machine-readable
12sig providers # table view sig providers --format json # machine-readable
1sig rename my-jira my-service # rename provider ID
123sig remove my-jira # remove provider + credentials sig remove my-jira --keep-config # clear credentials only sig remove my-jira --force # skip confirmation
Manages SSH remotes for credential sync across machines.
12345sig remote add prod ssh://deploy@ci.example.com sig remote add prod ci.example.com --user deploy --ssh-key ~/.ssh/id_rsa --path ~/.sig sig remote remove prod sig remote list sig remote list --format json
Syncs credentials over SSH. Sign in on your laptop, push to servers.
1234sig sync push prod # push all credentials to remote sig sync pull prod # pull credentials from remote sig sync push prod --provider my-jira # sync one provider only sig sync push --force # overwrite on conflict
Manages the auto-refresh watch list. The watch loop itself runs as part of the proxy daemon — use sig proxy start to keep sessions alive automatically.
1234sig watch add my-jira # add to watch list sig watch add my-jira --auto-sync prod # auto-sync after refresh sig watch remove my-jira # remove from watch list sig watch set-interval 1h # change default interval
Runs a local MITM HTTP/HTTPS proxy daemon. Agents point HTTP_PROXY/HTTPS_PROXY at the proxy and make normal requests — credentials are injected transparently and the agent never sees token values. The proxy also runs the watch/refresh loop.
12345678910sig proxy start # start daemon (default port 7891) sig proxy start --port 8080 # use custom port sig proxy stop # stop daemon sig proxy status # show running state, port, env var hints sig proxy trust # print CA cert path + OS trust instructions # Usage: point any tool at the proxy export HTTP_PROXY=http://127.0.0.1:7891 export HTTPS_PROXY=http://127.0.0.1:7891 curl https://jira.example.com/api/me # credentials injected automatically
When to use proxy vs sig run: Use sig run for wrapping a single command. Use sig proxy for long-lived daemons, tools that fork process trees, or tools that only read proxy env vars.
Inject rules: For APIs that need credentials in non-standard locations (body fields, query parameters, custom headers), add proxy.inject rules to the provider config. The proxy and sig request apply these rules after standard credential headers.
12345678910# Example: inject xoxc token from localStorage as form body parameter providers: app-slack: # ... domains, strategy, etc. proxy: inject: - in: body # header | body | query action: set # set | append | remove name: token from: credential.localStorage.xoxc-token
The from field resolves paths against the stored credential: credential.cookies, credential.accessToken, credential.localStorage.<key>. Body injection supports application/json and application/x-www-form-urlencoded content types.
Auto-refresh: The proxy daemon runs the watch/refresh loop automatically. Configure which providers to watch in config.yaml:
1234567watch: interval: 5m # check interval (default: 5m) providers: jira: autoSync: # optional: sync to remotes after refresh - dev-server ms-teams:
Credentials are refreshed before they expire. Use sig watch add <provider> to manage the watch list, or edit the config directly. The proxy's built-in watch loop replaces the need for a separate sig watch start process.
Trusting the CA: For HTTPS interception, the proxy generates a local CA certificate. Add it to your system trust store:
123456789101112sig proxy trust # show CA cert path + instructions # macOS sudo security add-trusted-cert -d -r trustRoot \ -k /Library/Keychains/System.keychain ~/.sig/proxy/ca.crt # Ubuntu/Debian sudo cp ~/.sig/proxy/ca.crt /usr/local/share/ca-certificates/sigcli-proxy.crt sudo update-ca-certificates # Per-command (no system trust) curl --cacert ~/.sig/proxy/ca.crt https://api.example.com
123sig completion bash >> ~/.bashrc sig completion zsh >> ~/.zshrc sig completion fish > ~/.config/fish/completions/sig.fish
Use sig proxy start to run the watch loop as a background daemon — it keeps credentials fresh and handles HTTPS interception simultaneously.
sig run injects SIG_<PROVIDER>_* variables into the child process. Use sig run my-jira -- env | grep SIG_MY_JIRA_ to discover exactly what's available for a provider.
When using the proxy (sig proxy start), set HTTP_PROXY=http://127.0.0.1:<port> and HTTPS_PROXY=http://127.0.0.1:<port> — credentials are injected by the proxy and no SIG_* variables are needed.
12345678910111213141516# Always present (example: provider "my-jira") SIG_MY_JIRA_PROVIDER # provider ID: "my-jira" SIG_MY_JIRA_CREDENTIAL_TYPE # credential type: cookie | bearer | api-key | basic # Bearer / OAuth2 token SIG_MY_JIRA_TOKEN # raw token value, e.g. "eyJ..." SIG_MY_JIRA_AUTH_HEADER # complete Authorization header value # Cookie credentials SIG_MY_JIRA_COOKIE # full cookie string, e.g. "SESSION=abc; ..." # With --expand-cookies: individual cookies SIG_MY_JIRA_COOKIE_SESSION=abc123 SIG_MY_JIRA_COOKIE_CSRF_TOKEN=xyz
Example: reading credentials inside a Python script:
1234567891011import os # Python script run via: sig run my-jira -- python fetch.py token = os.environ.get("SIG_MY_JIRA_TOKEN") cookie = os.environ.get("SIG_MY_JIRA_COOKIE") auth_type = os.environ.get("SIG_MY_JIRA_CREDENTIAL_TYPE") if auth_type == "cookie": headers = {"Cookie": cookie} elif auth_type == "bearer": headers = {"Authorization": f"Bearer {token}"}
Credential values are redacted from child stdout/stderr by default. Use --no-redaction to see raw values during debugging.
Sigcli reads ~/.sig/config.yaml. Run sig init to generate a starter file. The top-level keys are mode and providers.
1234567891011# ~/.sig/config.yaml mode: browser # browser | browserless browserChannel: chrome # chrome | msedge | chromium (default: chromium) providers: my-jira: url: https://jira.example.com strategy: cookie requiredCookies: - SESSION
1234567891011providers: <provider-id>: url: <base-url> # required: URL to match strategy: cookie|oauth2|api-token|basic requiredCookies: # wait until these cookies appear - SESSION cookiePaths: # extra paths for path-scoped cookies - /wiki forceVisible: true # always open visible browser (default: false) waitUntil: networkidle # load event: load|domcontentloaded|networkidle ttl: 8h # credential TTL (e.g. 1h, 8h, 7d)
Provider IDs (e.g. my-jira) are how you reference a provider in all commands. sig login auto-creates a provider entry when you pass a URL; use --as to set a custom ID.
A strategy implements IAuthStrategy: validate, authenticate, refresh, and applyToRequest. Sigcli ships four built-in strategies. Auto-detection picks the right one; use --strategy to override.
Captures the cookie jar from a real browser session. Best for SSO sites like Any site with multi-step login (QR codes, SAML, MFA). Supports forceVisible, waitUntil, requiredCookies, and cookiePaths (for sites that set auth cookies on a sub-path like /wiki).
1sig login https://jira.example.com --strategy cookie
Watches for Authorization: Bearer ... on outgoing requests, or decodes a JWT from an OAuth redirect. Auto-refreshes when a refresh token is present.
1sig login https://jira.example.com --strategy oauth2
For static API keys or Personal Access Tokens you already have. No browser needed — ideal for CI/CD.
1sig login https://jira.example.com --token <your-pat>
Username and password, encoded to a Basic auth header at request time. The plaintext password is stored only in the sealed credential file under ~/.sig/credentials/.
1sig login https://jira.example.com --username alice --password hunter2
Strategies return Result<T, AuthError> — never throw for expected failures. Callers check isOk() / isErr(). Build custom strategies by implementing IAuthStrategyFactory.
Sigcli abstracts the browser behind IBrowserAdapter — three small classes: Adapter → Session → Page. Two adapters ship in the box.
playwright-core with Chromium, Chrome, or Edge. Supports headless and visible modes. Required for browser SSO.sig init --remote puts Sigcli into browserless mode, where the NullBrowserAdapter is used — token/cookie/basic login still works, but SSO flows are disabled.
1234# Which adapter to use? # → Developer laptop with display: playwright (default) # → Attach to open Chrome: chrome-cdp # → Headless CI / remote server: browserless mode + sig sync pull
Write a custom adapter by implementing IBrowserAdapter, IBrowserSession, and IBrowserPage. Lazy-import the browser library and throw BrowserLaunchError on import failure so sig doctor can diagnose what's missing.
Lightweight client SDKs wrap the sig get CLI call, parse the JSON output, and return typed credential objects. They are thin wrappers — all auth logic lives in the CLI.
1npm install @sigcli/sdk
12345678910import { SigClient } from '@sigcli/sdk'; const sig = new SigClient(); // Get credential headers const headers = await sig.getHeaders('my-jira'); const response = await fetch('https://jira.example.com/api/me', { headers }); // Or use sig.request() directly const result = await sig.request('https://jira.example.com/api/issues/123');
1pip install sigcli-sdk
12345678910from sigcli import SigClient sig = SigClient() # Get headers headers = sig.get_headers("my-jira") response = requests.get("https://jira.example.com/api/me", headers=headers) # Or use sig.request() directly result = sig.request("https://jira.example.com/api/issues/123")
The SDKs require sig to be installed and a valid credential to exist. They call sig get --format json internally and parse the result — no browser or network access in the SDK itself.
Sigcli exposes a stable CLI surface that agents shell out to. No SDK, no MCP server — just commands with predictable exit codes and JSON output.
The recommended pattern is sig run: the agent spawns a child process with credentials already in the environment. The agent never sees token values. For tools that can't be wrapped — long-lived daemons, tools that fork — use sig proxy start instead.
1234567891011121314# Recommended: sig run keeps credentials out of agent context sig run my-jira -- python fetch_issues.py sig run my-jira -- node export_sprint.js # Alternative: sig proxy for daemons / tools that only read proxy env vars sig proxy start export HTTP_PROXY=http://127.0.0.1:7891 HTTPS_PROXY=http://127.0.0.1:7891 # now any tool that respects proxy env vars gets credentials injected # Discovery: find out what SIG_<PROVIDER>_* vars are available sig run my-jira -- env | grep SIG_MY_JIRA_ # Alternative: sig request (credentials stay internal) sig request https://jira.example.com/api/me
For Claude Code, the bundled /auth skill wraps these commands. To check auth status before acting:
12345# Always check before logging in — avoid unnecessary browser launches sig status my-jira --format json # exit 0 + "valid": true → credentials ready, skip login # exit 3 → no credentials, run sig login # exit 0 + expired → sig logout my-jira && sig login https://jira.example.com
The CLI owns locking, TTL, and refresh logic. Shelling out means every caller benefits from those without re-implementing them.
Never display sig get output in agent context or logs — it may contain raw bearer tokens or API keys.
Skills are drop-in packages that give AI agents (Claude Code, Cursor, Windsurf, Cline) authenticated access to specific services. Each skill is a directory with a SKILL.md guide and Python helper scripts. The agent reads the guide, shells out to the scripts, and sigcli handles auth transparently.
12345678# Install all skills (auto-detects your agent) ./skills/install.sh # Or pick an agent ./skills/install.sh --agent cursor # List what's installed ./skills/install.sh --list
sigcli-auth — Authentication guide. Strategy selection, command reference, error recovery. Bundled with every install.outlook — Read, send, search, reply, forward emails via Microsoft Graph. Uses ms-graph provider (OAuth2).msteams — Messages, conversations, people search, calendar via Teams Chat API + Graph. Uses ms-teams and ms-graph providers.slack — Channels, messages, search, reactions via Slack Web API. Uses app-slack provider (cookie + localStorage).See skills/sigcli-auth/references/config-template.yaml for a ready-to-use config with all providers pre-configured.
A skill is a directory with one file: SKILL.md. Add helper scripts if the agent needs to parse complex API responses. Here's the full recipe.
1. Create the directory
1mkdir -p skills/my-service/scripts
2. Write SKILL.md
The frontmatter tells the agent when to trigger. The body teaches it how to use the service.
12345678910111213141516171819202122232425262728293031323334353637--- name: my-service description: 'Interact with My Service — create, list, and update items. Use this skill when the user mentions My Service, items, or tasks.' --- # My Service Create, list, and update items via My Service REST API. ## Authentication | Provider | Type | Login Command | |------------- |--------|----------------------------------------| | `my-service` | cookie | `sig login https://my-service.example.com/` | **Run scripts:** ```bash sig run my-service -- python3 scripts/list_items.py \ --cookie "$SIG_MY_SERVICE_COOKIE" ``` ## Scripts ### list_items.py List items from the API. ``` --cookie TEXT Auth cookie (from sig run) --limit INT Max results (default: 20) --query TEXT Filter by keyword (optional) ``` ## Error Handling | Code | Meaning | Fix | |------|--------------------------|----------------------------------| | 401 | Session expired | `sig login <url>` | | 429 | Rate limited | Wait and retry |
3. Add a helper script (optional — only if parsing is non-trivial)
12345678910111213141516171819202122232425262728#!/usr/bin/env python3 """List items from My Service API.""" import argparse, json, sys, requests BASE = "https://my-service.example.com/api/v1" def list_items(cookie, query=None, limit=20): headers = {"Cookie": cookie} if cookie else {} params = {"limit": limit} if query: params["q"] = query resp = requests.get(f"{BASE}/items", headers=headers, params=params, timeout=15) resp.raise_for_status() return {"items": resp.json().get("items", []), "count": len(resp.json().get("items", []))} def main(): p = argparse.ArgumentParser() p.add_argument("--cookie", default="") p.add_argument("--query") p.add_argument("--limit", type=int, default=20) args = p.parse_args() try: json.dump(list_items(args.cookie, args.query, args.limit), sys.stdout, indent=2) except requests.HTTPError as e: json.dump({"error": f"HTTP_{e.response.status_code}"}, sys.stdout, indent=2) if __name__ == "__main__": main()
4. Add a test
12345678910111213141516"""Tests for my-service/scripts/list_items.py""" import re, responses from test_helpers import load_script mod = load_script("my-service", "list_items") class TestListItems: @responses.activate def test_basic_list(self): responses.get( url=re.compile(r"https://my-service\.example\.com/api/v1/items"), json={"items": [{"id": 1, "name": "Task A"}]}, ) result = mod.list_items("session=abc", limit=10) assert result["count"] == 1 assert result["items"][0]["name"] == "Task A"
5. Register and install
1234# Add to skills/install.sh ALL_SKILLS list, then: ./skills/install.sh # Test: cd skills && python -m pytest my-service/tests/ -v
That's it. The agent reads SKILL.md, calls sig run my-service -- python3 scripts/list_items.py, and gets JSON back. No SDK, no framework — just a markdown file and a script.
Skill conventions:
sig run env vars: SIG_<PROVIDER>_COOKIE or SIG_<PROVIDER>_TOKEN"") so scripts work with both sig run and sig proxyresponses library to mock HTTPinstall.sh excludes tests/ when copying to agent directoriesSign in on your laptop, push credentials to headless servers over SSH. No daemon required — sync uses your existing SSH keys and the same file locking as local storage.
12345678910111213141516# 1. On the headless server — set browserless mode sig init --remote # 2. On the laptop — add the remote sig remote add prod ssh://deploy@ci.example.com # or with explicit options: sig remote add prod ci.example.com --user deploy --ssh-key ~/.ssh/id_rsa # 3. Push all credentials to the server sig sync push prod # 4. Push a single provider sig sync push prod --provider my-jira # 5. On the server — use immediately, no browser needed sig run my-jira -- python deploy.py
Pull credentials from a remote machine (e.g. in a CI job that should mirror your dev credentials):
12sig sync pull prod # pull all sig sync pull prod --force # overwrite on conflict
Keep credentials fresh on the server by pairing watch with auto-sync:
123# On laptop: refresh my-jira every hour and auto-push to prod sig watch add my-jira --auto-sync prod sig watch start --interval 1h
Sync transports credential files as-is over SSH — the transport never decodes them. You keep your normal SSH key management; no new infrastructure to run.
All commands exit with a code that scripts can branch on:
123456# Exit codes 0 Success 1 GENERAL_ERROR — invalid args or unexpected failure 2 PROVIDER_NOT_FOUND — URL/ID doesn't match any configured provider 3 CREDENTIAL_NOT_FOUND — no stored credentials → run sig login 4 REMOTE_NOT_FOUND — SSH remote not configured → run sig remote add
Auth error codes from --verbose stderr:
1234567891011121314151617181920212223242526CREDENTIAL_EXPIRED # token/cookie expired, refresh failed # fix: sig logout <p> && sig login <url> CREDENTIAL_TYPE_MISMATCH # wrong credential type for provider # fix: re-login with --strategy <name> REFRESH_FAILED # OAuth2 refresh token rejected # fix: sig logout <p> && sig login <url> BROWSER_LAUNCH_ERROR # playwright-core not installed or no browser # fix: sig doctor; install playwright-core BROWSER_TIMEOUT # browser auth took too long # fix: retry; if CAPTCHA/MFA ensure visible mode BROWSER_UNAVAILABLE # machine is in browserless mode # fix: use --token/--cookie or sig sync pull CONFIG_ERROR # malformed ~/.sig/config.yaml # fix: sig doctor to validate SYNC_CONFLICT # local/remote credentials differ # fix: sig sync pull --force STORAGE_ERROR # cannot read/write credential files # fix: check permissions on ~/.sig/credentials/
Add --verbose to any command to see detailed error messages on stderr. Use sig doctor as the first step when something doesn't work.