peek is one Rust binary. It is built for agents first: it never prompts, it prints exactly one JSON value with --json, and every error says what failed, why, and the command that fixes it. Humans get the same commands with readable output.
The CLI is a tree you can walk. peek --help shows the branches, peek <command> --help shows what a command is for, how it is used with its neighbours, and then its flags. peek commands --json prints the whole tree as data, and peek docs <topic> prints these pages offline.
Command tree#
peek
├── iam [--json] IAM discovery; offline; works before login
├── login [<SLT>] [--token-file <PATH|->] [--recover] exchange an IAM short-lived token
│ └── status [--json] live-verified identity; {"authenticated":bool,…}
├── logout revoke this home's peek session (+ Ting grant)
├── config
│ ├── set '<json-object>' strict merge; prints the resulting config
│ ├── show | get <key> | unset <key>
│ ├── telemetry on|off
│ └── home <DIR> move this home's store (pointer file in the default home)
├── ting
│ └── enroll (re)register this Silicon as a Ting recipient for peek
├── register
│ ├── side <1-8> claim or move to a position (1 top, clockwise)
│ └── drawing <FILE.js> [--check] [--preview <OUT.png>] [--dump-frame <N>]
├── unregister release the position and delete the drawing (local + server)
├── send [--speak <TEXT>] [--show <JSON|@FILE|->] [--ask <JSON|@FILE|->]
│ [--voice <aura-2-name-lang>] [--lang <bcp47>] [--duration <SECS>] [--expires-in <SECS>]
│ [--notify speech_finished,show_dismissed] [--wait[=<SECS>]]
├── ask
│ ├── get <ASK_ID> local state and answer (works while Ting is not set up)
│ ├── list [--state pending|answered|dismissed|expired|cancelled] [--limit N]
│ └── cancel <ASK_ID> slide the ask away; no ting
├── history [--limit N] [--before <ID>] local send history for this Silicon
├── status position, drawing, queue, deliveries, app, daemon (one view)
├── org
│ └── byo deepgram (set --key-file <PATH|-> [--base-url URL] | show | delete) org admins only
├── app
│ └── status | install | update | uninstall Peek.app (install = ensure_app + launch)
├── daemon
│ └── status | restart
├── docs [<TOPIC>] [--search <TEXT>] [--all] bundled offline manuals
├── commands [--json] machine-readable command tree
├── report <MESSAGE> [--pr <URL>] [--via backend|gh] [--attach-status]
├── update Honeycomb guidance JSON (never self-replaces)
├── doctor diagnostics with exact fixes
└── --version | --help
Global flags and environment#
| Flag | Env | Meaning |
|---|---|---|
--json | – | Exactly one JSON value on stdout on success. On failure stdout is empty and one error object goes to stderr. |
--org <ORG> | SILICON_ORG | Org for org-specific calls. Precedence: --org, then SILICON_ORG, then the session's org_id. An empty value is invalid. |
--test <ENV_UUID> | SILICON_PEEK_TEST | Use a saved testing environment (Testing environments). A secret passed here is refused. |
--app-secret-file <PATH|-> | – | peek's test app secret; - reads one line from stdin. |
--app-secret <ask_…> | PEEK_TEST_APP_SECRET (hidden in help) | Same; conflicts with --app-secret-file. |
--api <URL> | PEEK_API_URL | peek backend origin. Default https://backend.peek.teamofsilicons.com. HTTPS only, except http on loopback. |
--idempotency-key <K> | – | Overrides the generated key of peek ting enroll and peek report only (16–255 visible ASCII). Every other command refuses it with conflicting_flags, because login, refresh and logout derive their keys so that a retry replays the same request. |
--no-telemetry | PEEK_TELEMETRY, SPACE_STATION_TELEMETRY, SILICON_TELEMETRY (any of 0/false/off/no) | Telemetry off for this process. The environment variables are also forwarded to the helper for this home (config.sync), so its later work for the home (deliveries, speech, drawing uploads) is not recorded either; the flag is not (Telemetry). |
-q, --quiet | NO_COLOR is honoured | No human hints or Next: lines on stderr (errors are still printed). peek never colours its output. |
Other environment the CLI reads:
| Variable | Use |
|---|---|
SILICON_HOME | The store root: state lives in $SILICON_HOME/.peek/. Unset falls back to your real home (~/.peek/). Empty or non-existent is an error (invalid_silicon_home). |
ISI | Optional. Recorded with each send and echoed as metadata.isi in Ting events. At most 160 characters on one line. Never grants authority. |
PEEK_DAEMON_SOCKET | Tests and isolated runs only: overrides the helper's socket path (Development). |
SILICON_HONEYCOMB | The Honeycomb binary peek doctor checks, instead of the one on PATH or ~/.honeycomb/dir/system/bin. |
The CLI never reads IAM_TEST_APP_SECRET or IAM_TEST_KEY (those belong to Ting), and never prompts. A value that must come from stdin (-) is refused when stdin is a terminal, and only one flag per command may read stdin.
Output and errors#
- Human mode prints readable text on stdout. Hints and
Next:lines go to stderr, and only in human mode. - With
--json, stdout carries one compact value. Object keys are printed in alphabetical order; the examples on this page are ordered for reading. Errors go to stderr as one object:{"error":{"code":"side_taken","message":"position 3 is held by si:dj; peek gives each Silicon exactly one position.", "hint":"choose a free one: peek register side 1 (free: 1,4,6,7)","retryable":false, "request_id":null,"details":{"owner":"si:dj","free":[1,4,6,7]}}} - Human-mode errors read
error: <message>,hint: <hint>,help: peek <path> --help,request: <id>. - A missing required argument prints that command's full help after the error. With
--json, a parse error is one error object instead. - When a testing environment is selected, the last stderr line is always
Testing environment: <name> (<uuid>). stdout stays pure JSON. - If your drawing fell back on screen, the next
peek send,peek register sideorpeek statusresult carries adrawing_fallback_activewarning with the error message and stack. It is attached once; other commands do not carry it, andpeek statusshowsdrawing.active: falseuntil you register a drawing again. - Results that can carry notes have a
warningsarray of{"code","message","details"?}. In human mode each warning is also printed to stderr aswarning: <message> (<code>).
Exit codes#
| Exit | Meaning | Codes |
|---|---|---|
| 0 | success, including login status answering authenticated:false | – |
| 1 | internal or unexpected | internal_error, store_corrupt, unexpected_response, ting_key_conflict, ting_rejected |
| 2 | usage or invalid input | invalid_input, invalid_json, conflicting_flags, nothing_to_send, text_too_long, caption_too_long, question_too_long, speak_too_long, too_many_elements, too_many_options, image_unreadable, image_too_large, image_unsupported, drawing_too_large, unknown_config_key, invalid_silicon_home, slt_is_public_id, idempotency_key_required, idempotency_conflict, frame_too_large, payload_too_large, telemetry_rejected |
| 3 | not authenticated | not_logged_in, session_rejected, slt_rejected, login_attempt_expired, reconsent_required, testing_secret_invalid, testing_generation_changed, unauthenticated, home_token_mismatch, authority_required |
| 4 | refused, or a precondition is missing | side_not_registered, drawing_not_registered, side_taken, drawing_invalid, slot_busy, ask_not_found, platform_unsupported, cli_outdated, store_schema_newer, private_application_organization_required, recipient_not_registered, not_org_admin, unknown_op, daemon_running, daemon_identity_mismatch, drawing_not_found, environment_not_prepared, idempotency_response_expired, not_found, lifecycle_conflict, origin_not_allowed, telemetry_table_unavailable |
| 5 | unavailable or transport | backend_unavailable, iam_unavailable, iam_misconfigured, daemon_unavailable, peek_service_unavailable, no_gui_session, app_update_pending, speech_unavailable, ting_unavailable, ting_type_missing, idempotency_in_progress, rate_limited, protocol_error |
telemetry_rejected, origin_not_allowed and telemetry_table_unavailable come from the backend's telemetry gateway, and lifecycle_conflict from its testing-environment lifecycle endpoints; no peek command normally shows them. A code this build does not know (a newer backend or helper may add some) is passed through unchanged, and its exit code follows the HTTP status: 401 → 3; 403, 404, 409 → 4; 400, 413, 422 → 2; 408, 429, 5xx → 5; anything else → 1. peek report --via gh uses gh_failed when gh itself fails (exit 5, or 1 when the report could not even be handed to gh).
retryable:true in an error means the same command can succeed later without changes. Exit 5 is usually worth retrying with backoff (no_gui_session and protocol_error are not); exits 2 to 4 need a change first.
peek iam#
Static discovery. No network, no session, no side effects, exit 0. Stemcell calls it for every candidate binary.
peek iam --json
{"app_id":"peek","org_id":"tos","name":"Peek","version":"0.1.0","api_version":"v1",
"api_url":"https://backend.peek.teamofsilicons.com","iam_url":"https://backend.iam.teamofsilicons.com",
"auth_url":"https://auth.iam.teamofsilicons.com","login_method":"short_lived_token","credential_issuer":false,
"login":"Mint an SLT with `iam silicon-login --app-id peek --grant-org <org> --approve-scopes` (Silicon) or `iam login --app-id peek --grant-org <org>` (Carbon), then run `peek login '<SLT>'`.",
"docs_url":"https://peek.teamofsilicons.com/docs","repository_url":"https://github.com/teamofsilicons/silicon-peek",
"rust_package":"silicon-peek-client","cli_package":"silicon-peek-cli","install":"honeycomb install 'peek'",
"platforms":{"full":["macos-aarch64","macos-x86_64"],"iam_only":["linux-x86_64","linux-aarch64","windows-x86_64","windows-aarch64"]}}
api_url follows --api, PEEK_API_URL or config api_url when set. With --test, it also includes "testing":{"environment_id","name","generation"}, read from the backend.
peek login#
Exchanges an IAM short-lived token (SLT) for this home's peek session. The SLT is single use and lives two minutes.
peek login "$SLT"
printf %s "$SLT" | peek login --token-file - # reads one line, strips the trailing newline
peek login --recover # retry a lost exchange within 10 minutes
- Mint the SLT with
iam silicon-login --app-id peek --grant-org <org> --approve-scopes(a Silicon) oriam login --app-id peek --grant-org <org>(a Carbon). - There is no interactive prompt. Without an SLT,
--token-fileor--recoverthe command fails withinvalid_input(exit 2,details.missing_argument:"SLT") and prints its help. - The exchange is idempotent: its key is derived from the SLT, and transport failures and 5xx are retried with the same key three times (after 1 s, 3 s and 9 s).
- On success it enrolls you as a Ting recipient, attaches this home to a running helper, and (on a Mac) installs and starts Peek.app in the background after printing its result. Its output and exit code never depend on that last step.
- Output: the authenticated
login statusshape below. If Ting enrollment did not complete,tingis{"subscribed":false,…,"error":{"code","message"}},nextis"peek ting enroll", and a hint says so on stderr. slt_rejected(exit 3): the SLT was used, expired or invalid. Mint a new one.slt_is_public_id(exit 2): a public ID such assi:xworks as an SLT only in a testing environment.login_attempt_expired(exit 3):--recoverran more than 10 minutes after the first attempt. Mint a new SLT.
peek login status#
peek login status --json
| Case | stdout | Exit |
|---|---|---|
| No session, or logged out | {"authenticated":false,"id":null,"reason":"no_session"|"logged_out","api_url":…,"testing_environment_id":…} | 0 |
| Session rejected by IAM | {"authenticated":false,"id":null,"reason":"rejected","rejection":{"code","at","request_id"},"api_url":…,"testing_environment_id":…,"next":"si auth setup peek (or: iam silicon-login … --app-id peek …; peek login '<SLT>')"} | 0 |
| Valid (verified live against the backend) | shape below | 0 |
| Backend or IAM unreachable | nothing on stdout; stderr {"error":{"code":"backend_unavailable"|"iam_unavailable",…,"retryable":true}} | 5 |
{"authenticated":true,"id":"si:dj","actor":{"type":"silicon","public_id":"si:dj"},"display_name":"DJ",
"org_id":"tos","org_ids":["tos"],"membership_id":"si:dj[tos]","authority":"silicon","custody":"client",
"scopes":["obo:ting:subscriptions.register","obo:ting:subscriptions.revoke","obo:ting:tings.send","self.identity.read","self.membership.read","self.profile.read"],
"reconsent_required":false,"access_expires_at":"2026-09-26T10:30:00Z","logged_in_at":"2026-09-26T10:00:00Z",
"family_expires_at_estimate":"2029-03-15T10:00:00Z","refresh_pending":false,
"ting":{"subscribed":true,"subscription_id":"sub_…"},
"daemon":{"attached":true,"queued_answers":0,"authority_required":0},
"testing_environment_id":null,"api_url":"https://backend.peek.teamofsilicons.com",
"store":"/abs/SILICON_HOME/.peek","validated":true}
display_nameisnullwhen IAM discloses none.nextis added when there is something to do: the re-login command whenreconsent_requiredis true, or"peek ting enroll"whenting.subscribedis false.- A missing helper never changes
authenticated; it only setsdaemon.attached:false. The helper is never asked whether you are authenticated. family_expires_at_estimateislogged_in_at + 900 days, an estimate because IAM does not report refresh expiry.
peek logout#
Revokes this home's peek session and its Ting recipient grant, then cancels this Silicon's undelivered answers on this Mac.
peek logout
Prints {"authenticated":false,"remote_revocation":"confirmed"|"pending"} and exits 0 in both cases. pending means the local session is gone but the backend could not be reached; the revocation is retried by the next peek run. A home without a session also answers confirmed. peek logout --help exits 0 (Stemcell probes it).
peek config#
Configuration for this home, stored in $SILICON_HOME/.peek/config.json.
peek config set '{"notify":["speech_finished"],"voice":"aura-2-thalia-en"}'
peek config show --json # {"schema":1,"telemetry":true,"voice":"aura-2-thalia-en","language":null,"notify":["speech_finished"],"api_url":null,"delivery_max_age_hours":168}
peek config get voice --json # {"key":"voice","value":"aura-2-thalia-en"}
peek config unset voice
peek config telemetry off
set parses strictly: a non-object, duplicate keys or an unknown key fail with exit 2 and details.valid_keys, and nothing is written. The object is merged atomically; null resets a key to its default. The resulting config is printed (the same shape as show) and pushed to a running helper. The helper gets the home's effective telemetry, which is false also when only the environment opts out. unset and telemetry are shortcuts for set and print the same. Stemcell runs peek config set after every login when app_configs.peek exists.
| Key | Type / validation | Default | Effect |
|---|---|---|---|
telemetry | bool | true | this home's telemetry |
voice | ^aura-2-[a-z]+-(en|es|de|fr|nl|it|ja)$ or null | null (per-language default) | default TTS voice |
language | BCP 47 primary subtag (2–3 letters) or null | null (detect) | TTS language when detection is ambiguous |
notify | array ⊆ ["speech_finished","show_dismissed"] | [] | default --notify |
api_url | https URL or null | null | same as --api |
delivery_max_age_hours | int 1..168 | 168 | how long this Silicon's undelivered events keep retrying |
peek config home <DIR> moves this home's store to <DIR>/.peek (the session, config, testing environments and daemon token) and leaves a pointer file named home in the default store. It prints {"home","store","pointer","moved":[…],"previous_store"}; pointer is null when you point back to $SILICON_HOME. If the target already holds a different store, nothing is changed (invalid_input).
peek ting enroll#
Registers (or re-registers) this Silicon as a Ting recipient for peek. Login does this automatically; run it yourself when peek login status shows "ting":{"subscribed":false} or a delivery is stuck on recipient_not_registered. peek never re-enrolls on its own, because that would undo a grant you revoked on purpose.
peek ting enroll --json # {"subscribed":true,"subscription_id":"sub_…"}
peek register side#
peek register side 5 --json
# {"slot":{"index":5,"side":"bottom"},"moved_from":null,"hotkey":"ctrl+cmd+5","warnings":[]}
Positions: 1 top, 2 top-right, 3 right, 4 bottom-right, 5 bottom, 6 bottom-left, 7 left, 8 top-left. One per Silicon: registering another number moves you (moved_from names the old one) and your drawing keeps running. A position held by another Silicon fails with side_taken (exit 4) and details:{"owner","free":[…]}.
hotkey is the Carbon's shortcut for the position: ctrl+cmd+<position> by default, or whatever modifier the Carbon chose in Peek.app Settings (for example opt+cmd+5).
peek register drawing#
Validates, stores and activates your bubble's JavaScript drawing. The file is resolved against the current directory and must be at most 256 KiB.
peek register drawing ./cassette.js
peek register drawing ./cassette.js --check # validate only; keep the active drawing
peek register drawing ./cassette.js --preview out.png # also write a PNG grid of test frames
peek register drawing ./cassette.js --dump-frame 30 # add test frame 30's display list
Human output:
✓ loaded (3.1 KB)
✓ 90/90 frames ok p50 0.31ms p95 0.58ms max 0.92ms
✓ ops/frame max 212
✓ glass rebuilt 1 time in 90 frames
drawing active for silicon "dj" at slot 5 (bottom)
--json:
{"sha256":"…","bytes":3174,"stats":{"frames":90,"p50_ms":0.31,"p95_ms":0.58,"max_ms":0.92,"ops_max":212,"glass_rebuilds":1},
"warnings":[],"logs":[],"active":true,"slot":5,"server_sync":"pending"}
activeisfalsewith--check.server_syncispendingwhen a copy is queued for the backend,skippedotherwise.slotis omitted while you hold no position.- With
--preview,previewnames the PNG written. With--dump-frame N(test frames are numbered 0–89),dumpholds that frame's display list (Drawing the visual).
A failing drawing exits 4 with drawing_invalid and the previous drawing stays active; details.error carries message, stack, frame and input_summary. Validation runs inside Peek.app with the same engine that draws on screen, so the CLI starts the app if needed (it can take up to 120 s on a cold start). The full API is in Drawing the visual.
peek unregister#
Releases your position and shortcut, deletes your drawing locally and on the server, and cancels your pending asks. No Ting events are sent.
peek unregister --json # {"released_slot":3,"cancelled_asks":["ask_0192…"]}
released_slot is null when you held no position.
peek send#
Speaks, shows or asks on the Carbon's screen. Returns immediately.
| Flag | Meaning |
|---|---|
--speak <TEXT> | 1–2000 characters, spoken with Deepgram Aura-2. |
--show <JSON|@FILE|-> | 1–3 text or image elements. See Speak and show. |
--ask <JSON|@FILE|-> | One question: text, single_choice, multiple_choice, slider or range. See Ask a question. |
--voice <aura-2-name-lang> | Overrides config voice, which overrides the per-language default. Needs --speak. |
--lang <bcp47> | Forces the TTS language instead of detecting it. Needs --speak. |
--duration <SECS> | 1–120. How long a show stays when there is no speech, or after the speech ends. Needs --show or --speak; not with --ask. |
--expires-in <SECS> | 10 s to 7 days, asks only. An unanswered ask then slides away and you receive peek.ask.expired. |
--notify speech_finished,show_dismissed | Opt in to those events for this send (default: config notify). |
--wait[=<SECS>] | Asks only. Keep the command open for the answer (default 120, at most 600). Write --wait=60, with the =; a bare --wait means 120. |
Rules, checked before anything is shown:
- At least one of
--speak,--show,--ask(nothing_to_send, exit 2). --showand--askare mutually exclusive, and the flags above must match what you send (conflicting_flags, exit 2).@FILEand-read the JSON from a file (relative to the current directory) or stdin. Image paths inside are also resolved against the current directory, and the CLI reads the bytes itself.- Unknown JSON fields and duplicate keys are rejected (
invalid_input,invalid_json). Lengths count Unicode scalar values. - You need a position and a drawing (
side_not_registered,drawing_not_registered, exit 4).
{"send_id":"snd_0192…","ask_id":"ask_0192…"|null,"slot":3,"status":"showing"|"queued",
"speech":{"status":"pending"|"cached"|"skipped"|"unsupported_language","model":"aura-2-thalia-en","chars":42}|null,
"warnings":[{"code":"speak_language_unsupported","message":"…"}]}
speech is null when there is no --speak.
With --wait, the command prints the final answer instead:
// answered in time (no Ting event is sent for this answer)
{"ask_id":"ask_…","send_id":"snd_…","state":"answered","answer":{"kind":"single_choice","option_id":"keep","label":"Keep"},"via":"click","transcript":null,"answered_at":"…Z"}
// dismissed, expired or cancelled while waiting
{"ask_id":"ask_…","send_id":"snd_…","state":"dismissed"}
// timeout: exit 0, and the answer is delivered by Ting later
{"ask_id":"ask_…","send_id":"snd_…","state":"pending","delivery":"ting"}
Queueing is per position: a new show replaces a visible show; while an ask is pending, up to 5 new sends queue behind it, then slot_busy (exit 4). Your ISIs share this queue.
peek ask#
Local state of your asks. Works even when Ting delivery is not set up.
peek ask get ask_0192… --json
# {"ask_id":"ask_0192…","send_id":"snd_0192…","question":"Keep the file?","ask_type":"text","state":"answered",
# "answer":{"kind":"text","text":"yes"},"via":"voice","transcript":"yes","answered_at":"…Z",
# "created_at":"…Z","expires_at":null,"delivery":{"status":"accepted","ting_id":"msg_…","silent":false}}
peek ask list --state pending --limit 20 --json # {"asks":[…]}, newest first, same item shape
peek ask cancel ask_0192… --json # {"ask_id":"ask_0192…","state":"cancelled"}
- States:
pending,answered,dismissed,expired,cancelled. delivery.status:pending,authority_required,accepted,expired,cancelled,failed, orwait(delivered to a livesend --waitinstead of Ting).send_id,question,ask_type,transcript,created_atandexpires_atare present when known.ask cancelslides the question away and sends no Ting event. On an ask that is already closed it returns that ask's current state. An unknown id fails withask_not_found(exit 4).ask list --limitis 1–200 (default 20).
peek history#
Your recent sends on this Mac, newest first. History never leaves the Mac.
peek history --limit 50 --json
# {"items":[{"send_id":"snd_…","kind":"ask","created_at":"…Z","closed_at":"…Z","close_reason":"answered","ask_id":"ask_…","ask_state":"answered"},
# {"send_id":"snd_…","kind":"speak","created_at":"…Z","closed_at":"…Z","close_reason":"auto",
# "warnings":[{"code":"speech_failed","message":"speech failed before any audio played: …"}]}]}
--limitis 1–200 (default 50); page with--before <SEND_ID>.- An item carries a
warningsarray (the same{"code","message"}shape as everywhere else) when something went wrong afterpeek sendhad already returned. Today that isspeech_failed(the speech could not be played, so the text showed as a pill instead) orspeech_unavailable(speech is not available for this Silicon or org right now, for example an org key that stopped working). The field is left out when there is nothing to report. In human mode the codes follow the row in brackets, such as[speech_failed].
peek status#
One view of everything that matters for this Silicon on this Mac: position, drawing, queue, pending asks, deliveries, Peek.app and the helper.
peek status --json
# {"actor_id":"si:dj","context":"production",
# "slot":{"index":3,"side":"right"},"drawing":{"sha256":"…","bytes":3174,"active":true,"last_error":null},
# "queue":{"pending":0},"pending_asks":1,"deliveries":{"pending":0,"authority_required":0,"last_error":null},
# "ui_running":true,"daemon":{"running":true,"version":"0.1.0","protocol":1},"app":{"build":1000,"ui_running":true}}
slotanddrawingarenulluntil you register them.drawing.activeisfalsewhile the fallback visual shows.deliveries.authority_requiredgreater than 0 means answers are waiting for a valid session or Ting enrollment;peek login statusandpeek doctorsay which.warningsappears when there is a pending note such asdrawing_fallback_active(attached once, to the firstpeek send,peek register sideorpeek statusafter the fallback).
peek org byo deepgram#
Org admins can make peek use the org's own Deepgram key for every member's speech and transcription. The key is stored sealed on the backend and never returned.
peek --org tos org byo deepgram set --key-file - --base-url https://api.eu.deepgram.com < key.txt
peek --org tos org byo deepgram show --json # {"configured":true,"org_id":"tos","updated_at":"…Z","base_url":"https://api.eu.deepgram.com"}
peek --org tos org byo deepgram delete # {"configured":false,"org_id":"tos","updated_at":null,"base_url":null}
--base-urlmust be anhttps://origin (host and optional port; no credentials, path or query; a trailing/is dropped) on a Deepgram host the backend allows. The backend operator sets the allowed hosts withPEEK_BYO_DEEPGRAM_HOSTS(comma-separatedhost,*.domainor*); the default is*.deepgram.com, sohttps://api.eu.deepgram.comworks everywhere.http://, IP addresses andlocalhostare always refused, whatever the allowlist says. The CLI refuses a non-https URL, an IP address,localhost, credentials or a query before calling the backend (invalid_input); the backend refuses a host outside its allowlist withinvalid_inputanddetails.reason: "base_url_not_allowed". Leave--base-urlout to usehttps://api.deepgram.com.- The allowlist is checked again every time the key is used. If the operator narrows it later, a stored base URL outside it makes speech fail with
speech_unavailableandreason: "org_base_url_not_allowed". peek never quietly swaps in its own key; an admin sets the key again with an allowed--base-url. - The key must have Deepgram's Member role or higher; it is validated before it is saved.
- Once set there is no silent fallback to peek's key: a key that stops working makes speech fail with
speech_unavailableand areason(org_key_invalid,org_out_of_credits,org_model_forbidden,org_base_url_not_allowed). - Non-admins get
not_org_admin(exit 4). - These commands only call the backend, so they work on Linux and Windows too.
peek app#
Manage Peek.app on this Mac.
| Command | Does | --json |
|---|---|---|
peek app status | Whether Peek.app is installed, its build and signature, whether it and the helper run, the background item, the offered builds and the install log. | {"supported":true,"platform","installed","path","build","short_version","bundle_id","signature":"valid"|"invalid"|"unchecked"|"unreadable"|"absent","running":{"ui","daemon"},"daemon":{…}|null,"agent":"loaded"|"not_loaded"|"unknown","best_offer","bundled","last_install_script":[…]}. On Linux and Windows: {"supported":false,"platform":"linux-x86_64"}, exit 0. |
peek app install | Installs ~/Applications/Peek.app from this CLI's package if it is missing, verifies its signature, removes quarantine and starts it. It never modifies an existing app. | {"installed":true,"path","installed_now","offered_build","build","short_version","running":{"daemon":true,"ui"},"peekd_version"} |
peek app update | Offers the newest build this CLI knows to the helper, which installs it once nothing is on screen. Needs a logged-in home. | {"scheduled","installed_build","offered_build"} (plus reason when nothing is offered) |
peek app uninstall | Unregisters the login item and helper and moves Peek.app to the Trash, then waits up to 15 s for it to go. honeycomb uninstall 'peek' does not do this. | {"uninstalled":true,"was_installed","path","via":"peekd"|"open"|null} |
peek daemon#
peek daemon status --json
# {"running":true,"pid":812,"version":"0.1.0","protocol":1,"socket":"/var/tmp/silicon-peek-501/peekd.sock","ui":{"running":true,"build":1000},"homes":3}
peek daemon restart --json
# the same shape, plus "restarted":true and "via" (how it was restarted)
daemon status needs no login and never starts anything. When the helper is not running it prints {"running":false,"socket":"…","next":"peek app install"} and exits 0. restart asks launchd to restart the helper, or starts Peek.app when the agent is not loaded.
peek docs#
The pages on this site, bundled into the binary so they work offline.
peek docs # the topic list (JSON: {"topics":[…],"online","repository","help","index_content",…})
peek docs ask # one topic as Markdown
peek docs --search keyterm # line matches across all topics (JSON: {"query","results":[{…,"matches":[{"line","excerpt","truncated"}]}]})
peek docs --all # everything (JSON: {"documents":[…]})
peek docs ask --json # {"topic","title","path","format":"markdown","command":"peek docs ask","content","package_version","embedded":true}
Topics: start, carbon, silicon, cli, show, ask, drawing, ting, iam, testing, telemetry, privacy, platforms, development, versioning, troubleshooting. An unknown topic fails with invalid_input and details.topics. Search excerpts are at most 320 characters.
peek commands#
peek commands --json
# [{"command":"peek send","description":"…","usage":"…","help":"…","group":false,
# "arguments":[{"name","long","short","positional","required","global","description","possible_values"}]}]
group is true for a command that only groups subcommands (such as peek register). The list is generated from the same definitions as --help, so it is always complete.
peek report#
Files a bug. If you also fixed it, pass your pull request.
peek report "send --ask fails with invalid_input when an option label has an emoji" \
--pr https://github.com/teamofsilicons/silicon-peek/pull/42 --attach-status
# {"submitted":true,"id":"rep_…","status":"filed"|"stored","issue_url":"https://github.com/teamofsilicons/silicon-peek/issues/…"|null,"via":"backend"}
- The backend stores the report and files a GitHub issue.
status:"stored"means it was saved but not (yet) filed. --prmust behttps://github.com/teamofsilicons/silicon-peek/pull/<n>. Without it, a hint on stderr invites you to send a fix.--attach-statusadds the non-secretpeek doctoroutput.--via ghfiles it with your ownghinstead (gh issue create --repo teamofsilicons/silicon-peek), and prints"id":null,"status":"filed","via":"gh". A missingghfails withnot_found(exit 4); a failingghwithgh_failed(exit 5).- Refused under
--testwithconflicting_flags(exit 2): reports go to the production tracker.
peek is open source. The best report says exactly how to reproduce the bug, and links the fix. See Development.
peek update#
peek never replaces itself. Honeycomb updates the CLI every minute, and the helper updates Peek.app. peek update tells you what is installed and how to update by hand:
{"manager":"honeycomb","app_id":"peek","current_version":"0.1.0","auto_update":true,"can_replace_running_binary":false,
"command":"honeycomb update 'peek'","app":{"build":1000,"best_offer":1000},"message":"Honeycomb manages the peek CLI: …"}
auto_update is false when HONEYCOMB_AUTO_UPDATE is off. app is null on Linux and Windows.
peek doctor#
Runs every check and prints the exact fix for anything that is wrong. It always exits 0; a check that cannot run is reported as a warning.
peek doctor --json
# {"checks":[{"name":"…","status":"ok"|"warn"|"fail","detail":"…","fix":"…"|null}, …],
# "summary":{"ok":9,"warn":1,"fail":0},"version":"0.1.0","platform":"macos-aarch64"}
Checks, by name: store (permissions), session, pending_login, revocations and ting (session, rejection and enrollment state), backend (/readyz), honeycomb (0.5.0 or newer), and on a Mac app (installed, signature, build), agent (the background item), install_log, daemon (socket and protocol), mic (permission, reported by Peek.app), hotkeys and outbox (the delivery backlog). See Troubleshooting.