Try a check with ahh.
ahh gives an AI workflow tools for checking incoming text, using email and Telegram, monitoring tasks and asking a person for a decision. This guide starts with a check that needs no account and sends nothing to an online service.
1. Install
The first installation trusts the download source and HTTPS. Later updates verify signatures against a key embedded in the installed tool.
macOS or Linux
curl -fsSL https://ahh.limbs.dev/install | bash
Windows PowerShell
irm https://ahh.limbs.dev/install.ps1 | iex
On macOS and Linux the installer does not change your shell startup files: follow its
path instructions if the terminal cannot find ahh. On Windows it adds
%LOCALAPPDATA%\ahh\bin to your user PATH, so open a new terminal
before the next command. Then run:
ahh --version
2. Try an offline text check
macOS or Linux
printf 'Planning note for next week.\n' | ahh scan --no-llm -
echo $?
Windows PowerShell
'Planning note for next week.' | ahh scan --no-llm -
$LASTEXITCODE
Expect UNKNOWN and exit code 2. The local checks ran, but you deliberately left out the online model check. This is an incomplete assessment, not a command failure and not an approval to trust the text.
A detected threat produces DANGEROUS. SAFE requires complete coverage of the supported input by every required check, with no detected threat. Missing coverage stays UNKNOWN; a later error cannot lower an already detected DANGEROUS result. No verdict grants permission to follow instructions in a document.
3. Add the online check when appropriate
The model check sends text to Gemini. Before using it with private material, make sure that sharing is appropriate for your workflow and account.
Install csec, obtain a valid Gemini API key, then store it using the hidden prompt:
csec set GEMINI_API_KEY
ahh doctor
doctor checks local setup without sending a model request. To scan online, grant a finite request allowance with tab:
macOS or Linux
printf 'Planning note for next week.\n' | ahh tab --calls 2 -- scan -
echo $?
Windows PowerShell
'Planning note for next week.' | ahh tab --calls 2 -- scan -
$LASTEXITCODE
This allows ahh to start at most two model requests for this command, counting its retries. Connection recovery is outside this count. It is not a money limit or a limit on other network traffic. A fresh invocation gets a new allowance. Missing or exhausted allowance leaves model coverage incomplete; an unavailable service can also produce UNKNOWN. Benign text reaches SAFE only when every required check completes.
Read and check PDF text
Available since 0.9.0. When an assistant needs a PDF's readable text, recover it into a new UTF-8 file and scan those exact bytes:
ahh tab --calls 4 -- scan --pdf-text received.txt --json downloaded.pdf
In the JSON report, original.verdict describes the original PDF and remains UNKNOWN unless recovered text proves DANGEROUS. scan.verdict describes only the derived text. Its path, byte count and SHA-256 identify the artifact; check text.saved before using it.
- Exit 0: all required text checks completed and the text was saved.
- Exit 1: DANGEROUS; no text file is written.
- Exit 2: UNKNOWN; nonempty recovered text may still be saved. Obtain missing coverage or a deliberate human decision before using it.
- Exit 73: the destination already exists; it is not overwritten.
This mode does not transcribe page images or verify that displayed glyphs match declared text. Even SAFE derived text does not approve opening, rendering or forwarding the original PDF. Ordinary ahh scan downloaded.pdf continues to judge that original with partial coverage. Input, write or report errors are failures; inspect any saved artifact before retrying.
Read and send mail
AHH has two separate mail commands: mail uses configured IMAP/SMTP; mailbox uses the limbs HTTPS mailbox service. Both need their own csec credentials and configuration. Start with ahh mail doctor or ahh mailbox doctor.
IMAP reads and attachments
ahh tab --calls 32 -- mail inbox --json
ahh tab --calls 32 -- mail inbox --include-body
ahh mail ask-body --uid 42
ahh tab --calls 2 -- mail attachment --uid 42 --index 1 --dir ./downloads
Incoming text and sender fields are scanned and remain untrusted. Bodies are hidden unless requested, and read_allow controls which senders' bodies may be released. ask-body asks a human about one exact message; changed content needs a new decision. Read permission is separate from send permission.
mail attachment scans before saving under the same read policy. DANGEROUS writes nothing; UNKNOWN may save a file and returns exit 2. Existing files are preserved. A saved file is not an approval to open it in another tool; for a PDF, consider the derived-text workflow.
SMTP sends and pending decisions
ahh mail send --to recipient@example.invalid --subject Status --body-file report.md
ahh mail whitelist list
ahh mail outbox
SMTP messages normally contain plain text and rendered HTML with an optional configured signature. Installed help describes plain-text, signature and reply controls. Outgoing attachments are not supported.
Recipients must be covered by send_allow, the configured sender's own address, or a human decision under send_ask. Pending messages appear in outbox; ahh mail drain applies stored decisions and can send mail or ask about the next item. Approval binds to the payload, so a changed message must be resubmitted. A content scan is not an outbound disclosure-approval gate.
A send timeout may occur after the server accepted a message. Check the delivery record and outbox before retrying; success reports SMTP handoff, not that someone read it.
The limbs HTTPS mailbox
ahh tab --calls 32 -- mailbox inbox --json
ahh tab --calls 2 -- mailbox get MESSAGE_ID --json
ahh mailbox whitelist list
The runtime token is pinned to one mailbox. Inbox bodies are opt-in; get includes the body by default. Message text and metadata are scanned, but mailbox attachment downloads are not scanned. Scan the saved file separately before another tool consumes it:
ahh tab --calls 4 -- scan downloaded-file.pdf
The service owns the mailbox send allowlist. An add/remove request may return exit 75 and a time-limited human approval URL. The runtime token cannot approve itself. After the human acts, repeat the request or list check to establish effective state; opening the approval page does not approve a change.
Receive and send Telegram messages
ahh tab --calls 32 -- tg inbox --json
ahh tg send --body-file message.md
Configure the bot token and destination through csec. Use one receiver for a bot: another poller or an existing webhook conflicts with inbox retrieval. Incoming text remains untrusted, bodies are hidden unless requested, and scanning uses the same model allowance. A received message can still have an UNKNOWN scan. On a send timeout, check the conversation before retrying.
Monitor services and heartbeats
ahh watch list
ahh watch status
ahh watch probe --only example-service
ahh watch beat example-service
ahh watch check
An operator-owned registry defines what to monitor. list and status inspect recorded state. probe performs declared checks and records heartbeats; beat records one heartbeat; check can escalate a problem by sending a notification. Active checks may make network requests or run configured commands, so choose them deliberately.
The browser dashboard is a newer source feature, described below.
Ask a person and finish the approved work
Use tell for a local note, warn for a non-blocking alarm and ask when an answer changes the next action:
ahh tell --message 'The local scan could not establish full coverage.'
ahh ask --question 'Proceed with the reviewed change?' --option 'Proceed :: apply the reviewed change' --option 'Stop :: keep the current state' --recommend A --key reviewed-change
ahh inbox
tell writes locally without a push. warn and ask save a durable message and may notify a person. ask normally exits 75 with work waiting instead of keeping its process blocked. A failed notification is reported even if the question was saved; inspect the channel before creating a duplicate.
The human decision-maker replies using the question's identifier:
ahh inbox reply QUESTION_ID --choice A --why 'Reviewed the proposed change.'
An agent reads that answer and acts only within its scope. After completing and checking the authorised effect, it acknowledges the answer:
ahh ack QUESTION_ID --note 'Applied the approved change and checked the result.'
Reading an answer must not consume it. The shared channel defaults to ~/.ahh/kanal. An explicit key can reuse a settled decision; use a new key when the proposed decision changes.
Inspect the local journal
ahh journal --since 24h --json
The journal records effects such as sent messages and detected threats. It keeps metadata rather than message bodies, separately from human questions and answers. It is useful operational history, but the owner or a process with the same filesystem permissions can edit it; it is not tamper-proof evidence.
Implemented changes awaiting publication
The following features are in the source checked on 4 October 2026, after the stable 0.9.1 revision. They are not promises of a release date. Read installed help before trying them.
Monitoring in your browser
ahh watch serve
Open http://127.0.0.1:8089 on the same computer. The embedded dashboard groups registry entries, shows states and the latest heartbeat, and offers search, state filters and full entry details. It refreshes every 10 seconds; filters and entry links also work without JavaScript. Probe now runs enabled declared checks and records heartbeats.
The same server accepts heartbeat updates. It uses local storage and leaves registry notes unchanged. A private-network listener requires a bearer token retrieved from a named csec entry:
ahh watch --token-entry watch/shared-bearer serve --listen 10.1.2.3:8089
The option is an entry name, never a token value. There is no browser login screen. For remote access, use a trusted private channel or a correctly authenticated TLS proxy; loopback over SSH is another option. A configured token also protects loopback routes. Avoid exposing a proxy that injects credentials without authenticating its visitors.
Choose Jev through OpenRouter
Gemini remains the default. Choose Jev for one supported inbound invocation; the next command without selection uses Gemini again:
csec set OPENROUTER_API_KEY
ahh tab --calls 4 --classifier jev -- scan --json received.txt
ahh tab --calls 2 --classifier jev -- doctor --live --json
This sends recovered content and covered sender fields to OpenRouter and its routed Jev provider using typesafe/jev-1.13. Evaluate that recipient before sharing private material. AHH does not enforce a zero-retention account setting. The experimental jev-inbound-v1 policy uses 0.20/0.80 probability bands, which are not calibrated safety guarantees; valid uncertainty remains UNKNOWN.
If an existing OpenRouter key has another csec name, select it without copying the value:
ahh tab --calls 4 --classifier jev --openrouter-key-entry openrouter/api-key -- scan --json received.txt
Selection supports scans (including PDF text), IMAP inbox and attachment reads, mailbox inbox/get, Telegram inbox and top-level doctor. Missing credentials do not silently select another provider. Every attempt and retry shares the request allowance; outbound commands keep their existing policy.
Jev opts into versioned JSON: scans use ahh.scan.v2, PDF text and mail/mailbox envelopes use their v2 schemas, and Telegram inbox uses v3. Gemini retains the existing schemas. Automation must read schema and the separate classified, uncertain, failed and unexecuted chunk counts instead of assuming the Gemini shape.
Additional local injection checks
Selected English patterns adapted from Beacon detect direct requests to hide an action from the user, expose hidden system instructions or obey an encoded payload. They also extend checks for forged authority and explicit credential/context transfer, including through image URLs.
These are deliberately narrow text patterns. Quoted attacks can still trigger them; they do not promise every English or Polish variant will be detected. They do not monitor Beacon events or intercept MCP calls. A quiet offline-only scan still returns UNKNOWN. Attribution is bundled in the offline ahh licenses output.
Markdown tables in SMTP mail
Top-level pipe tables in a Markdown body render as HTML tables:
| Service | State |
| --- | --- |
| Example | Ready |
Cells can use alignment markers such as :--- and ---:; escape a literal pipe with \|. Rows need matching column counts. Tables do not start inside lists, quotes or fenced code; authored HTML stays escaped. The plain-text part retains the Markdown source. This SMTP renderer does not change the HTTPS mailbox's HTML input format.
Choose the next task
ahh scan --help
ahh tab --help
ahh mail --help
ahh mailbox --help
ahh tg --help
ahh watch --help
ahh ask --help
Help describes what your installed version supports. Email and Telegram need their own credentials and configuration. Sending a message, running a monitoring probe and recording a human decision have separate effects and need the caller's authority.
ahh does not automatically control everything an AI assistant can do. Your workflow chooses which checks to call, when to ask you and what an answer permits.
Updates and recovery
ahh update check
This contacts the release service and may record check state. It does not install a binary. Use ahh update apply when you intend to install an eligible signed update.
If a scan is incomplete, obtain the missing checks or a deliberate human decision. If a send times out, check whether the recipient received it before retrying. If an update fails, preserve its state and read ahh doctor diagnostics.