Configure
The daemon reads ~/.post-cortex/daemon.toml at start. Environment variables override the file, and the file overrides the defaults.
host = "127.0.0.1"port = 3737data_directory = "/Users/you/.post-cortex/data"| Setting | Environment | Default |
|---|---|---|
host |
PC_HOST |
127.0.0.1 |
port |
PC_PORT |
3737 |
data_directory |
PC_DATA_DIR |
~/.post-cortex/data |
auth_token |
PC_AUTH_TOKEN |
none |
allowed_hosts |
PC_ALLOWED_HOSTS |
localhost, 127.0.0.1, ::1 |
| log level | RUST_LOG |
info |
[[hooks]] and [[modules]] are reloaded without a restart: the daemon looks at the file every 2 seconds. Other settings take effect at the next start.
Reaching it from other machines
Section titled “Reaching it from other machines”Bound to 127.0.0.1, only this computer reaches the daemon. To serve a tailnet or a LAN, bind to its address and set a token:
host = "100.64.0.7" # your tailnet addressauth_token = "<a long random string>"allowed_hosts = ["my-mac.tailnet.ts.net"]auth_token: every request exceptGET /health, and every gRPC call, must sendAuthorization: Bearer <token>. ThepcxCLI reads it from the same file. Without a token on such an address, the daemon warns at start.allowed_hosts: the names clients put in their URL. Requests with any otherHostheader get403, which stops a web page from reaching the daemon through a DNS name that points at it. An entry is a name or an IP, with an optional port (my-mac.tailnet.ts.net:3737). The allowlist is not authentication: use the token too.
Redaction
Section titled “Redaction”On by default. Before anything is stored, secrets and personal data are replaced with [REDACTED:<kind>]: private keys, JWTs, API keys of the common providers, bot tokens, bearer tokens, passwords in URLs, password= and similar values, emails, phone numbers and public IP addresses. Private and tailnet addresses, UUIDs and host names are kept: they are needed to connect events.
redact = true # PC_REDACT=0 turns it offredact_patterns = ['ORD-\d{6}'] # your own, replaced as [REDACTED:custom]The same value is always replaced the same way, so entities and relations stay consistent. Only counts per kind are logged, never the values.
Search
Section titled “Search”search_mode = "local" # fast, local or accurate: the default when a call names noneneighbour_radius = 1 # records on each side of a hit; 0 for hits onlyreranker_model = "cross-encoder/ms-marco-MiniLM-L4-v2" # the local rerankervoyage_api_key = "pa-..." # the accurate reranker, or VOYAGE_API_KEYvoyage_model = "rerank-3-lite"What the modes do and what each costs: Search. With voyage_api_key set, Voyage is available but off: each workspace turns it on (the app’s workspace settings, or gRPC UpdateWorkspace with remote_rerank). Only then does an accurate search there send its query and candidate texts to Voyage AI; elsewhere it uses the local reranker. On Linux the local reranker needs ONNX Runtime 1.31; point ORT_DYLIB_PATH at libonnxruntime.so. Without it, the daemon logs an error and searches without the local reranker.
Conversation capture
Section titled “Conversation capture”Records exist only when your agent writes them. Capture keeps the conversation too: every prompt and final answer, in a stream of the session apart from the records. It is off by default; pcx setup --capture turns it on for your clients. What it is for: Conversations.
message_retention_days = 90 # older turns are deleted with their vectors; 0 keeps themStorage
Section titled “Storage”The daemon stores everything in RocksDB in data_directory by default. Built with SurrealDB, it can use a SurrealDB server instead; a user other than root signs in as a namespace user, so the daemon does not need root:
USE NS post_cortex;DEFINE USER pcx ON NAMESPACE PASSWORD '...' ROLES OWNER;Move data between the two with pcx migrate.
Modules
Section titled “Modules”Files, a shell and git are built in, each off until [[builtins]] names it with what it may reach. Everything a module touches goes through the daemon’s sandbox: files only below its filesystem roots, processes under the operating system’s sandbox (Seatbelt on macOS, Landlock and seccomp on Linux), and the network only with the network grant.
[[builtins]]id = "tools-fs" # read, write, edit, list_dir, grep, globgrants = ["filesystem"]filesystem = [{ path = "/Users/you/project", write = true }]
[[builtins]]id = "tools-git" # status, diff, log, branches, switch, commitgrants = ["filesystem", "spawn"]filesystem = [{ path = "/Users/you/project", write = true }]config = { author_name = "Agent", author_email = "agent@example.com" }
[[builtins]]id = "board"Every module has its page under Modules, with its settings. A module of your own runs as a process; see writing a module.
[[modules]]id = "my-module"command = "/opt/pcx/my-module"sha256 = "<shasum -a 256 my-module>"grants = ["write_memory"]uses = ["post-cortex.memory"]The daemon refuses a module whose file does not match its sha256, and one that asks for a grant its entry does not list. uses names the modules whose tools it may call.
Hook scripts
Section titled “Hook scripts”The daemon runs your own hook scripts on your agents’ events, with Claude Code’s contract: the event as JSON on stdin, exit 0 with an answer, or exit 2 to block with stderr as the reason.
[[hooks]]id = "secrets-guard"command = "/Users/you/hooks/secrets-guard.py"sha256 = "<shasum -a 256 secrets-guard.py>"events = [{ event = "PreToolUse", policy = "fail-closed", deadline_ms = 2000 }]capabilities = ["read_conversation"]Only a fail-closed script may block; a fail-open one that fails or runs late is skipped. A script sees the conversation only with read_conversation.