Skip to content

Connect Claude Code

For synchronous workspace policy, local context and response checks, start with Live agent connections. That installer preserves the activity recording hooks described below. Sign-in and recording alone do not activate the synchronous loop.

On a Mac, start with Planisphere for Mac: its Memory view connects Claude Code and installs its memory hooks. The recording hooks on this page add activity reporting to your console workspace; after merging them, quit and relaunch Claude Code so they load.

The Claude Code hooks do two things.

  • They record. Each prompt you submit, each tool call Claude makes and each reply it finishes becomes a sealed record. Prompts, tool inputs and replies are hashed with SHA-256 on your machine; Planisphere receives the hashes, not the text. These hooks send in the background and never stop Claude Code.
  • They guard your boundaries. Before a tool runs, the crossing guard checks the call against the boundary policies your workspace saved on the console's Policies page. A call that crosses none goes ahead and sends nothing more. A call that crosses one asks Planisphere first, and a Hold or Wall policy stops it. See Boundary policies.

You can also opt in to Usage by prompt, a separate companion that measures token usage and visible UTF-8 text sizes locally, then reports private observations in Activity. Its optional prompt bridge retains an exact link to the original prompt receipt. The guide explains project scope, source attribution and incomplete counts.

The console writes the hooks for you. On the Connections page, under Connect a system, choose Claude Code, paste a key, and copy the block labelled Claude Code hooks (settings.json). Read the commands before you paste them anywhere.

What the hooks record

Every record uses the AI Provenance pack and the system name claude-code-primary.

Record (hook) Hashed on your machine Sent as plain metadata
prompt_submitted (UserPromptSubmit) The prompt text Model, session ID, working directory, agent type, a prompt counter
tool_used (PreToolUse, every tool) The tool's input, as compact JSON Tool name, tool class, session ID, agent type
agent_response (Stop) Claude's latest reply text, and the whole transcript file Model, session ID, stop reason, working directory, turn count, a reply counter

The fourth recording hook, SessionStart, sends nothing. It saves the session's model name in /tmp/planisphere/ so the prompt records can carry it. The crossing guard adds two more entries, one beside SessionStart and one beside PreToolUse; the next section describes them.

The tool class groups tool names: fs_read (Read, Glob, Grep), fs_write (Write, Edit, NotebookEdit), shell (Bash, BashOutput, KillShell), network (WebFetch, WebSearch), agent_dispatch (Task, Agent), skill (Skill) and other.

A few limits follow from where the hooks run:

  • Tool calls are recorded before they run. PreToolUse fires as Claude asks for the tool, so a tool_used record says the call was requested. Its success is pending and its duration_ms is 0: placeholders, not measurements. Tool output is not recorded.
  • The working directory is sent as written. Prompt and reply records carry the full path of the folder Claude Code runs in. If a path names a client or a project you keep private, use a neutral folder name.
  • The Stop hook reads your transcript locally. It finds Claude's latest reply in the session transcript and hashes it, and it hashes the transcript file as a whole. Neither leaves your machine as text.
  • The counters are per machine. system_seq comes from ~/.claude/planisphere-seq-prompt and ~/.claude/planisphere-seq-response, which the hooks increment. Two machines keep separate counts.

Boundary policies

A boundary policy names what crosses a boundary, who guards it and how Planisphere responds: Watch, Flag, Hold or Wall. You write them on the console's Policies page, starting from a starter or from nothing. Only a saved policy is enforced.

What crosses is one or more patterns, where * matches anything:

Pattern Matched against Example
Commands Each statement of a Bash command. cd repo && git push origin main is two statements. Leading env, sudo, time and VAR=value are ignored. git push*main*
Files The file a tool reads or writes. A name without / matches the file's name; with /, the end of its path. Case is ignored. .env*, config/*.pem
Web The host of an address a tool fetches. Case is ignored. api.stripe.com, *.stripe.com
Tools Any use of a tool, by its name. mcp__*stripe*

How the guard works:

  1. At session start, and then at most once a minute, it fetches your saved policies into ~/.claude/planisphere/rules.json (or into $PLANISPHERE_HOOK_DIR, if you set it).
  2. Before each tool runs, it checks the call against that copy, on your machine. A call that crosses no saved boundary goes ahead, and nothing more is sent.
  3. A call that crosses one asks Planisphere. It sends that call's command, file path or web address, and a SHA-256 of the whole tool input. It never sends file contents, edit text or tool output. Planisphere checks the call against the policies in force and seals the decision:
    • Watch lets it through and records the crossing.
    • Flag lets it through, records it, opens a review for the guard, and tells Claude it was flagged.
    • Hold stops it and tells Claude who it is waiting on. The guard approves or rejects it in Reviews, signed in. After an approval, the identical request goes through once: the same tool and input, in the same folder and session. The same request again is held again.
    • Wall stops it every time. No one can approve it.
  4. Only a person signed in to the console decides a held crossing: the person or role the policy names as its guard. A workspace key, like the one in these hooks, can't approve one, and can't save or remove a policy.

The command, path or address is kept as a readable fact of the crossing only while your workspace keeps readable details (Account). Otherwise only its hash is kept, and the guard decides without seeing it.

If Planisphere can't be reached (5 seconds, or 2 to connect), a call that crosses a Hold or Wall boundary is refused, and Claude is told why. A Watch or Flag crossing goes ahead and is kept in ~/.claude/planisphere/spool.jsonl, then sent at the next session start.

The guard never answers "allow": a call it lets through still goes through your own Claude Code permission settings. A policy you save reaches a running session within a minute.

The guard holds a cooperative agent to your policies; it doesn't contain one that works around them. It matches the text of a command, so a command wrapped in another shell (sh -c '…') or a script file can pass a pattern the plain command would cross. Anyone who edits ~/.claude/settings.json can remove the hooks. The witnesses on Connections, such as GitHub and your cloud audit log, are the independent check on what actually crossed.

Before you start

  • jq. The hooks use it to read each event Claude Code passes them. Check with command -v jq; on a Mac, brew install jq; on Debian or Ubuntu, sudo apt-get install jq.
  • curl and a SHA-256 tool. macOS has curl and shasum; most Linux systems have curl and sha256sum. The hooks use whichever is present.
  • A key from your workspace. Create one on the console's Keys page. Use a key just for this machine, for example claude-code-laptop, so you can revoke it without touching anything else.
  • macOS or Linux. Claude Code runs each hook command with sh -c: bash 3.2 in POSIX mode on macOS, dash on Debian and Ubuntu. The hooks are written for that plain POSIX shell, not for a newer bash.

The key ends up in your settings file as plain text, inside each hook command. Keep that file private, and never put these hooks in a settings file you commit to a repository.

Add the hooks

  1. In the console, open Connections and choose Claude Code under Connect a system.
  2. Paste your key into Key to fill in. It stays in that browser tab and fills into the hooks; nothing is sent.
  3. Copy the Claude Code hooks (settings.json) block.
  4. Open ~/.claude/settings.json, your user settings, which apply to every project on this machine. If the file doesn't exist, create it with the block as its whole content.
  5. If the file already has a "hooks" object, merge rather than replace: for each of SessionStart, PreToolUse, UserPromptSubmit and Stop, add the Planisphere entry to the event's list next to the entries you already have. Check the result is valid JSON with jq . ~/.claude/settings.json.
  6. Start a new Claude Code session. Claude Code's /hooks menu lists the hooks it loaded.
  7. To have Claude Code ask before crossing a boundary, save a policy on the console's Policies page.

To connect a single project instead, put the hooks in that project's .claude/settings.local.json, your own settings for that project, which are not meant to be committed. Avoid .claude/settings.json in a project: it is shared through the repository, and the hooks contain your key.

Check that records arrive

Send a prompt in the new session, then open the console:

  • Connections → Systems sending records lists claude-code-primary with its record count for the last 24 hours, when the last record arrived and its last event. The last event opens that record.
  • Activity lists the records your workspace sealed, newest first. Each opens with its receipt.

The hooks don't print action keys, so start from the Systems table rather than Check a received record. One received record shows that the hooks ran and reached your workspace once; it doesn't show that every prompt or tool call was captured.

Troubleshooting

Nothing arrives. The hooks send in the background and discard errors, so they never show a failure themselves. Planisphere does when it can tell whose records it refused: a revoked key still in use, the sandbox's record cap, or invalid or rate-limited records put a red "records refused" banner on the console's Connections page and a warning on its Connections tab, and the workspace's owners get an email, at most once a day for each problem. A key Planisphere never issued names no workspace, so that failure stays silent. Check, in order:

  1. command -v jq prints a path.
  2. jq . ~/.claude/settings.json prints your settings, not an error.
  3. You started a new session after adding the hooks, and /hooks lists them.
  4. The key is current. This prints 200 for a working key and 401 for a revoked or mistyped one:

    curl -sS -o /dev/null -w '%{http_code}\n' https://api.planisphere.ooo/tenant/me \
      -H "X-Planisphere-Key: YOUR_KEY"
    

Prompts and replies arrive, but no tool calls, or Claude Code reports a hook error on every tool. Your hooks are an older copy. Claude Code runs hook commands with sh -c, and on macOS that is bash 3.2 in POSIX mode, which can't parse a case pattern's closing ) inside $(...). The old PreToolUse command then stops with syntax error near unexpected token and exit status 2, which Claude Code treats as a block on that tool call. Copy the hooks again from Connections, or edit the PreToolUse command so each pattern opens with ( as well:

CLASS=$(case "$TN" in (Read|Glob|Grep) echo fs_read;; (Write|Edit|NotebookEdit) echo fs_write;; (Bash|BashOutput|KillShell) echo shell;; (WebFetch|WebSearch) echo network;; (Task|Agent) echo agent_dispatch;; (Skill) echo skill;; (*) echo other;; esac);

Some prompts or tool calls never arrive, often the ones with line breaks or backslashes. Your hooks are an older copy that passes each event to jq with echo "$INPUT". In sh (bash 3.2 in POSIX mode on macOS, dash on Debian and Ubuntu), echo turns the \n and other backslash escapes inside the event's JSON into real characters, so jq can't read it and the record is silently dropped. Copy the hooks again from Connections: the current hooks pass each value with printf '%s\n', which prints it unchanged. To fix a copy by hand, replace every echo "$INPUT" and echo "$LAST" with the same printf form, for example:

SID=$(printf '%s\n' "$INPUT" | jq -r .session_id);

Older Stop hooks fail differently: instead of dropping the record, they send a reply record whose response hash is the hash of empty text (sha256:e3b0c442…) and whose model is blank. Records like that, sealed before you updated the hooks, show an empty reply, not the real one.

A tool call is refused with "Planisphere couldn't be reached". It crosses a Hold or Wall boundary, and the guard couldn't get an answer: no network, a revoked key, or Planisphere unavailable. Check the key with the command above. The call goes through once Planisphere answers and the policy allows it.

A policy you saved isn't asked about yet. The guard's copy of your policies refreshes at session start and at most once a minute. Start a new session, or wait a minute. Check that the pattern matches the call as Claude Code writes it: patterns match each statement from its start, so git push* doesn't match git -C repo push.

Records stopped after a while. A sandbox workspace keeps up to 100 records, and the hooks send one for every prompt, tool call and reply, so a working session reaches that quickly. After that the API answers 402 and the hooks drop the record. Upgrade on the console's Billing page.

The model shows as unknown. The prompt record reads the model name that SessionStart saved in /tmp/planisphere/. If that file is missing (the session began before you added the hooks, or /tmp was cleared), the record says unknown. A new session fixes it.

Remove the hooks

Delete the six Planisphere entries from ~/.claude/settings.json (or from the project file you used), then start a new session. Remove /tmp/planisphere/, ~/.claude/planisphere/ and ~/.claude/planisphere-seq-* if you no longer want them, and revoke the key on the console's Keys page.

Hook events and settings files follow the official Claude Code hooks documentation.