claudecode Module
The claudecode module wires Claude Code hooks to Abysslink's notification pipeline so your phone buzzes when Claude stops or is waiting for input.
Architecture note
The claudecode module is one opt-in consumer of the generic notify
module. Claude Code is not a core dependency of Abysslink — the
underlying architecture is a generic phone-to-rig toolkit. This module
just wires Claude Code's hook system to Abysslink's notification pipeline.
What it configures
Abysslink merges JSON hook entries directly into ~/.claude/settings.json.
There is no hooks directory and no wrapper script. The merge:
- preserves every existing user hook (only abysslink's own entries are replaced),
- is idempotent — repeated
up --applyruns never duplicate entries, - is an audited write (backup + audit-log entry; the log records a hash, never the file body),
- refuses to overwrite a
settings.jsonthat is not valid JSON.
The hook commands embed the absolute path of the running abysslink binary
when it is safe to do so, because Claude Code's hook environment frequently
lacks the user's interactive PATH.
Hooks written
| Hook event | Command | When written |
|---|---|---|
Stop |
abysslink notify "Claude stopped" "Session ended" |
always |
Notification |
abysslink notify --claude-hook |
when notify_on.notification: true |
PreToolUse |
abysslink approve --check --blocking |
only when gate.enforcing: true |
PermissionRequest |
abysslink approve --permission-request |
only when gate.enforcing: true |
The Notification hook pipes Claude Code's notification JSON on stdin;
notify --claude-hook reads it, classifies the type, and titles the push
accordingly so you can tell what Claude wants at a glance:
| Claude's message contains | Push title | Priority |
|---|---|---|
permission / approve / allow |
Claude needs approval | max |
select / choose / option (a multiple-choice prompt) |
Claude needs a decision | high |
waiting / input / idle |
Claude needs input | high |
| anything else | Claude | high |
Claude's real message is the push body. Over the default self-hosted ntfy/UnifiedPush path this stays on your own server (no third party). The experimental direct APNs/FCM legs (off by default) would transit Apple/Google, so enable those only if you accept that the notification text leaves the tailnet.
The PreToolUse hook blocks tool execution pending phone or TTY approval;
PermissionRequest is a fast non-blocking notification trigger. Both are
written only when the approve gate is enforcing (it ships in shadow mode).
Configuration
claudecode: is a top-level key (not under modules:):
claudecode:
enabled: true # default: false
api_key_source: keychain # keychain | env | none (default: keychain)
notify_on:
notification: true # write the Notification hook
stop_after: "60s" # accepted by the schema; reserved — the Stop hook currently fires on every session end
The config loader uses strict decoding — keys other than these fail the whole config load.
API key handling
With api_key_source: keychain, abysslink up --apply seeds the Anthropic API
key from the $ANTHROPIC_API_KEY environment variable into the OS keychain
(service abysslink, account anthropic-api-key). The value is read from the
environment and delivered to the keychain via stdin — never placed on argv and
never logged. Rotate it later with:
abysslink doctor checks
All claudecode checks are warnings (doctor exits 1):
| Check | Meaning |
|---|---|
api_key_present |
no key in the keychain — set $ANTHROPIC_API_KEY and re-run up --apply |
api_key_keychain |
keychain not readable (login keychain may be locked after reboot) |
claude_dir_exists |
~/.claude/ missing — Claude Code not installed |
settings_json_exists / settings_json_readable |
~/.claude/settings.json missing or unreadable |
stop_hook_configured / notification_hook_configured |
abysslink notify hooks missing |
approve_pretooluse_hook_configured / approve_permissionrequest_hook_configured |
approve hooks missing (checked only when gate.enforcing: true) |
Removal
abysslink disable claudecode stops future applies; uninstall (and the
module's config-reversal path) surgically strips only the abysslink entries
from ~/.claude/settings.json, leaving every other user setting intact.
Usage flow
- Start a Claude Code session from your phone (SSH into your rig, run
claude) - Walk away — your phone receives a push notification when Claude stops or needs input
- Reconnect from the phone and continue the session
This is the "vibe-code Claude from your phone" workflow that Abysslink was built to enable.