Codex CLI is OpenAI's terminal-based coding agent. It reads, writes, and executes code locally while keeping your source on your machine. This guide shows how to give Codex persistent, cross-session memory through Remnic — so it remembers your projects, preferences, and past decisions every time you start a session.
Codex is stateless by default. Every session starts from zero — it doesn't know your project conventions, past debugging sessions, or architecture decisions unless you re-explain them. Remnic fixes this.
| Without Remnic | With Remnic |
|---|---|
| Re-explain project conventions every session | Codex recalls coding standards, naming patterns, and folder structure automatically |
| Repeat architecture context for every task | Entity knowledge surfaces DB schemas, API contracts, and module boundaries on demand |
| Lose debugging context between sessions | Past root-cause analyses and dead-end paths are recalled, avoiding repeated work |
| Manually state preferences (test runner, linter, git workflow) | Preferences persist across sessions and projects |
| Third-party integration details forgotten | Remnic entities store API details, service endpoints, and integration patterns |
| Context-switching tax when resuming work | Session-start recall brings you back to speed instantly |
- "Use snake_case for database columns" — Remnic recalls this convention before Codex generates a migration.
- "The payments module talks to Stripe via
PaymentGatewayservice" — Remnic surfaces this when Codex works on checkout code. - "Last time we debugged the N+1 query, the fix was eager-loading
user.orders" — Remnic prevents re-investigating the same issue. - "Joshua prefers pytest with strict typing and async-first design" — Remnic adapts Codex's suggestions to your style.
- Codex CLI v0.114.0+ (
codex --version) - Remnic HTTP server running and reachable (see API docs)
- A bearer token for authentication
- Node.js in your PATH (
node --version) — the bundled plugin hooks are a single cross-platform Node.js runner, so this is the only hard requirement on every platform. - Platform extras for the manual hook below:
- macOS / Linux: a POSIX shell plus
curl(andpython3only if you use the Python manual example). - Windows 10/11: Windows PowerShell 5.1 ships with the OS — nothing to install. PowerShell 7 (
pwsh) is optional.Invoke-RestMethodis built in, socurl/python3are not required. Use the PowerShell hook variant and%USERPROFILE%paths shown below.
- macOS / Linux: a POSIX shell plus
Cross-platform note (issue #1440): As of the cross-platform-hooks release, the bundled
@remnic/plugin-codexhooks ship a unified Node.js runner with bothcommand(POSIX.sh) andcommandWindows(PowerShell.ps1) entries, so the marketplace install andremnic connectors install codex-clinow work on Windows, macOS, and Linux with no manual hook scripting. The bundledhooks.jsonresolves the runner via${PLUGIN_ROOT}(which Codex injects and substitutes for plugin hooks), so it works regardless of the session's working directory. Prefer those paths; the manual setup further down is only for advanced/custom cases.
If you just want Remnic wired into Codex with sensible defaults, run:
remnic connectors install codex-cliThis writes a connector config, drops the Remnic memory extension
(memories_extensions/remnic/instructions.md) as a sibling of
<codex_home>/memories/, and runs a health check. Codex's phase-2
consolidation sub-agent auto-discovers the extension the next time it
runs — no further wiring needed.
To opt out of the memory extension (for users self-managing the Codex memories_extensions folder):
remnic connectors install codex-cli --config installExtension=falseTo target a non-default Codex home (for example an integration-test home or a shared multi-user install):
CODEX_HOME=/srv/codex remnic connectors install codex-cli
# or
remnic connectors install codex-cli --config codexHome=/srv/codexThe rest of this guide covers manual setup for more advanced cases.
Generate a secure token and add it to your shell profile (~/.zshenv, ~/.bashrc, etc.) on every machine where Codex or Remnic runs:
# Generate a token (or use any secure random string)
openssl rand -base64 32
# Add to your shell profile
export REMNIC_AUTH_TOKEN="<paste-generated-token-here>"Source the profile or open a new terminal so the variable is available.
On the machine where Remnic runs:
npx remnic-server \
--host 0.0.0.0 \
--port 4318 \
--auth-token "$REMNIC_AUTH_TOKEN"Use remnic daemon start only when Codex and Remnic run on the same machine, or
when your remnic.config.json already sets a non-loopback bind address under
server.host. The daemon helper defaults to 127.0.0.1, which is not reachable
from a second machine unless you change the config first.
If you have namespacesEnabled: true in your Remnic config, set the principal in
the config file rather than on the remnic-server command line. The standalone
server currently reads server.principal from config but does not expose a
--principal CLI flag.
cat > remnic.config.json <<EOF
{
"remnic": {
"namespacesEnabled": true
},
"server": {
"host": "0.0.0.0",
"port": 4318,
"authToken": "${REMNIC_AUTH_TOKEN}",
"principal": "generalist"
}
}
EOFFor persistent operation, set up a launchd plist (macOS), systemd unit (Linux), or similar service manager so the server survives reboots.
Edit ~/.codex/config.toml:
[mcp_servers.remnic]
url = "http://<remnic-host>:4318/mcp"
bearer_token_env_var = "REMNIC_AUTH_TOKEN"Replace <remnic-host> with:
127.0.0.1if Codex and Remnic run on the same machine- The machine's LAN IP or Tailscale IP for cross-machine access
Verify with:
codex mcp listYou should see remnic in the URL-based servers section with Bearer token auth.
The hook automatically recalls relevant Remnic memories at the start of every Codex session, injecting them as context before your first message.
Most users should skip this section and use the bundled plugin (marketplace or
remnic connectors install codex-cli), which ships a maintained cross-platform hook. The manual script below is for custom setups. Pick the variant for your OS.
Create the hook script at ~/.codex/scripts/remnic-session-recall.sh:
#!/usr/bin/env bash
# Codex SessionStart hook: recall Remnic context at session start.
set -euo pipefail
REMNIC_HOST="${REMNIC_HOST:-127.0.0.1}"
REMNIC_PORT="${REMNIC_PORT:-4318}"
REMNIC_TOKEN="${REMNIC_AUTH_TOKEN:-${OPENCLAW_REMNIC_ACCESS_TOKEN:-${OPENCLAW_ENGRAM_ACCESS_TOKEN:-}}}"
REMNIC_URL="http://${REMNIC_HOST}:${REMNIC_PORT}/engram/v1/recall"
# Read hook input from stdin
INPUT="$(cat)"
# Extract cwd from hook payload to build a meaningful recall query
CWD="$(echo "$INPUT" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('cwd',''))" 2>/dev/null || echo "")"
PROJECT_NAME="$(basename "$CWD" 2>/dev/null || echo "unknown")"
QUERY="Starting a new coding session in project: ${PROJECT_NAME}. Recall relevant memories, preferences, decisions, and context about this project and the user."
# If no token, skip gracefully
if [ -z "$REMNIC_TOKEN" ]; then
echo '{"continue":true,"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"[Remnic: no token set — skipping memory recall]"}}'
exit 0
fi
# Call Remnic recall API
RESPONSE="$(curl -s --max-time 8 \
-X POST "$REMNIC_URL" \
-H "Authorization: Bearer ${REMNIC_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"query\": $(echo "$QUERY" | python3 -c "import json,sys; print(json.dumps(sys.stdin.read().strip()))"), \"topK\": 12}" \
2>/dev/null || echo "")"
# Extract the context field
if [ -n "$RESPONSE" ]; then
CONTEXT="$(echo "$RESPONSE" | python3 -c "
import json, sys
try:
d = json.load(sys.stdin)
ctx = d.get('context', '')
count = d.get('count', 0)
if ctx:
print(f'[Remnic Memory Recall — {count} memories]\n\n{ctx}')
else:
print('[Remnic: no relevant memories found for this session]')
except Exception:
print('[Remnic: recall response parse error]')
" 2>/dev/null || echo "[Remnic: recall failed]")"
else
CONTEXT="[Remnic: server unreachable — continuing without memory recall]"
fi
# Return hook output with additionalContext
python3 -c "
import json, sys
context = sys.stdin.read()
output = {
'continue': True,
'hookSpecificOutput': {
'hookEventName': 'SessionStart',
'additionalContext': context
}
}
print(json.dumps(output))
" <<< "$CONTEXT"Make it executable:
chmod +x ~/.codex/scripts/remnic-session-recall.shWindows has no bash, curl, python3, or chmod by default — and there is no chmod step (PowerShell scripts are run by the interpreter, not by an executable bit). Create the hook at %USERPROFILE%\.codex\scripts\remnic-session-recall.ps1 instead. It uses the built-in Invoke-RestMethod, so no extra tools are needed:
#requires -Version 5.1
# Codex SessionStart hook: recall Remnic context at session start.
$ErrorActionPreference = 'Stop'
$remnicHost = if ($env:REMNIC_HOST) { $env:REMNIC_HOST } else { '127.0.0.1' }
$remnicPort = if ($env:REMNIC_PORT) { $env:REMNIC_PORT } else { '4318' }
$token = $env:REMNIC_AUTH_TOKEN
if (-not $token) { $token = $env:OPENCLAW_REMNIC_ACCESS_TOKEN }
if (-not $token) { $token = $env:OPENCLAW_ENGRAM_ACCESS_TOKEN }
$url = "http://${remnicHost}:${remnicPort}/engram/v1/recall"
# Read the hook payload from stdin.
$raw = [Console]::In.ReadToEnd()
$cwd = ''
try { $cwd = (ConvertFrom-Json $raw).cwd } catch {}
$project = if ($cwd) { Split-Path -Leaf $cwd } else { 'unknown' }
function Emit($context) {
@{ continue = $true; hookSpecificOutput = @{ hookEventName = 'SessionStart'; additionalContext = $context } } |
ConvertTo-Json -Compress -Depth 5
}
if (-not $token) { Emit '[Remnic: no token set — skipping memory recall]'; exit 0 }
$query = "Starting a new coding session in project: $project. Recall relevant memories, preferences, decisions, and context about this project and the user."
$body = @{ query = $query; topK = 12 } | ConvertTo-Json -Compress
try {
$resp = Invoke-RestMethod -Method Post -Uri $url -TimeoutSec 15 `
-Headers @{ Authorization = "Bearer $token" } -ContentType 'application/json' -Body $body
if ($resp.context) {
Emit ("[Remnic Memory Recall — $($resp.count) memories]`n`n" + $resp.context)
} else {
Emit '[Remnic: no relevant memories found for this session]'
}
} catch {
Emit '[Remnic: server unreachable — continuing without memory recall]'
}Add the hook to ~/.codex/hooks.json (macOS/Linux) or %USERPROFILE%\.codex\hooks.json (Windows). Provide both command (POSIX) and commandWindows (PowerShell) so the same config works everywhere — Codex picks the right one per platform:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "<full-path-to>/remnic-session-recall.sh",
"commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File <full-path-to>\\remnic-session-recall.ps1",
"timeout": 15
}
]
}
]
}
}Replace <full-path-to> with the absolute path on each platform — e.g. /Users/you/.codex/scripts/remnic-session-recall.sh (macOS/Linux) and C:\Users\you\.codex\scripts\remnic-session-recall.ps1 (Windows). powershell is Windows PowerShell 5.1, which ships with Windows 10/11; if you've installed PowerShell 7 you can substitute pwsh.
You can add instructions to your AGENTS.md (global or project-level) to guide Codex on when to use Remnic tools:
## Memory
Remnic is available as an MCP server for long-term memory.
- Use `remnic.recall` for targeted lookups on specific topics.
- Use `remnic.memory_store` to persist durable facts, preferences, decisions, and corrections.
- Use `remnic.entity_get` to look up known entities (people, projects, tools, companies).
- If the Remnic MCP server is unavailable, proceed without memory.Once connected, Codex has access to these Remnic MCP tools:
| Tool | Purpose |
|---|---|
remnic.recall |
Retrieve relevant memories for a query |
remnic.recall_explain |
Debug the last recall (what was retrieved and why) |
remnic.memory_get |
Fetch a specific memory by ID |
remnic.memory_timeline |
View a memory's lifecycle history |
remnic.memory_store |
Store a new explicit memory |
remnic.suggestion_submit |
Queue a memory suggestion for review |
remnic.entity_get |
Look up a known entity by name |
remnic.review_queue_list |
View the governance review queue |
If Remnic runs on a different machine than Codex (e.g., a home server), use a VPN or overlay network like Tailscale to make the HTTP server reachable:
- Install Tailscale on both machines
- Use the Remnic host's Tailscale IP in
config.tomland the hook script - Set
REMNIC_HOSTin the hook script to the Tailscale IP
The hook script gracefully degrades — if the Remnic server is unreachable or the token is missing, it skips recall and lets the session proceed normally.
After setup, start a new Codex session and check:
- MCP tools available: Ask Codex to list its available tools — you should see
remnic.*tools. - Recall working: The session should start with
[Remnic Memory Recall — N memories]context if the hook is configured. - Writes working: Ask Codex to store a test memory:
Use remnic.memory_store to save "test memory from Codex" with category "fact" and dryRun true. It should return"status": "validated".
Remnic tools not showing up in Codex:
- Codex loads MCP servers at session start. If the server was down when the session started, restart Codex.
- Verify with
codex mcp list— remnic should showenabledwithBearer tokenauth. - Check the server is reachable:
curl -s http://<host>:4318/mcp -X POST -H "Authorization: Bearer $REMNIC_AUTH_TOKEN" -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
"namespace is not writable" error:
- You have
namespacesEnabled: truein your Remnic config. Setserver.principalinremnic.config.jsonso it matches awritePrincipalsentry for your target namespace.
Hook not firing:
- Verify
~/.codex/hooks.json(%USERPROFILE%\.codex\hooks.jsonon Windows) exists and uses the correct absolute path. - macOS / Linux: ensure the script is executable (
chmod +x) and thatnodeis on PATH (the bundled hooks are Node.js). - Windows: there is no
chmodstep. Make sure thecommandWindowsentry is present and thatpowershell(Windows PowerShell 5.1, ships with Windows;pwshfor PowerShell 7) andnodeare on PATH. If scripts are blocked by policy, the-ExecutionPolicy Bypassflag in the example handles it; you do not need to change machine policy. - Check that
REMNIC_AUTH_TOKENis set in your environment. The olderOPENCLAW_REMNIC_ACCESS_TOKENandOPENCLAW_ENGRAM_ACCESS_TOKENnames are still accepted during the compatibility window.
Slow session start:
- The hook has a 15-second timeout. If the Remnic server is slow to respond, increase the
timeoutvalue inhooks.jsonor reducetopKin the recall query.
Codex's phase-2 consolidation reads canonical files under <codex_home>/memories/
(memory_summary.md, MEMORY.md, raw_memories.md, rollout_summaries/*.md).
Remnic can mirror hot memories into this exact layout so the always-loaded
memory_summary.md is populated with Remnic content — giving Codex a quick
cross-session pass without any MCP roundtrips.
Materialization is opt-in via a sentinel file (<codex_home>/memories/.remnic-managed):
if the sentinel is missing, Remnic will skip the directory and log a warning,
so hand-edited layouts are never clobbered. Writes are atomic (temp dir +
rename), and idempotent no-ops happen whenever the content hash is unchanged.
Triggers (all configurable):
- After semantic or causal consolidation completes (
codexMaterializeOnConsolidation) - At Codex session end via the bundled Stop hook (
codexMaterializeOnSessionEnd) - Manually:
tsx scripts/codex-materialize.ts --reason manual
See plugins/codex.md — Native memory materialization for the full list of config knobs and the opt-out procedure.