Private codex-ds4

Use Codex as the coding-agent interface while DeepSeek runs locally through DwarfStar, with no cloud model key and no non-local network route.

Last updated

Local Inference Privacy Codex CLI

1. What this profile is

codex-ds4 is a local launcher name, not an official Codex subcommand. Codex supplies the terminal UI, project instructions, shell tools, and edit loop. DwarfStar loads the model and exposes a Responses-compatible HTTP endpoint on loopback.

project → Nono → Codex CLI → 127.0.0.1:8000 → ds4-server → local model files policy agent UI Responses API inference
BoundaryDecision
Model trafficOnly 127.0.0.1:8000
Cloud credentialsNot loaded; the local bearer value is a placeholder
Codex stateDedicated CODEX_HOME, separate from normal Codex
Writable filesystemThe current project and dedicated local state; Nono still exposes essential system and tool paths read-only
NetworkBlocked by Nono except the single loopback port
Telemetry and updatesDisabled in the isolated Codex config and blocked at the network layer
Local inference is not a sandbox. A local model can still delete files, run unsafe commands, or follow malicious project instructions. The external Nono boundary is what limits reach.

2. Install Codex, Nono, and ds4

This walkthrough targets Apple Silicon. DwarfStar also supports CUDA and ROCm, but the build command and model fit differ.

Codex and Nono

brew install --cask codex
brew install nono

codex --version
nono --version

DwarfStar

xcode-select --install  # skip if already installed

mkdir -p "$HOME/src"
git clone https://github.com/antirez/ds4.git "$HOME/src/ds4"
cd "$HOME/src/ds4"
make

Keep the runtime checkout and model directory outside your coding projects. The Codex client does not need access to either one; only the separately launched server does.

3. Choose a model

For interactive coding on a 96 or 128 GB Mac, start with the resident DeepSeek V4 Flash Q2 model. It is much smaller and faster than the newer V4.1 checkpoint when V4.1 must stream experts from SSD.

TargetPublic downloadPractical use
DeepSeek V4 Flash 0731 Q2./download_model.sh ds4f-q2Recommended first coding profile on 96/128 GB
DeepSeek V4.1 Flash 1017 Q2./download_model.sh ds41f-q2Large quality experiment; 341 GiB file and SSD streaming on one 128 GB Mac
cd "$HOME/src/ds4"
./download_model.sh ds4f-q2

The downloader updates ds4flash.gguf to the selected main model. For reproducible experiments, pass an explicit -m path instead of relying on whichever download ran last.

4. Start the local server

Start with 32K context. Larger agent prompts cost memory and prefill time; raise the server and client limits together only after the basic profile works.

mkdir -p "$HOME/.cache/ds4-server-kv"
cd "$HOME/src/ds4"

./ds4-server \
  --ctx 32768 \
  --host 127.0.0.1 \
  --port 8000 \
  --kv-disk-dir "$HOME/.cache/ds4-server-kv" \
  --kv-disk-space-mb 8192

For V4.1 on one 128 GB Mac, select the exact file and add SSD streaming:

./ds4-server \
  -m gguf/DeepSeek-V4.1-Flash-1017-IQ2_XXS-Q2_K-imatrix.gguf \
  --ssd-streaming \
  --ctx 32768 \
  --host 127.0.0.1 \
  --port 8000
Do not add --trace for normal use. DwarfStar traces can contain rendered prompts, generated text, cache decisions, and tool events. The disk KV cache can also contain prompt text and model state; treat it as private and disposable.

Confirm the server without sending a prompt:

curl -fsS http://127.0.0.1:8000/v1/models

5. Create an isolated Codex home

Do not reuse the normal ~/.codex directory. A separate home prevents this profile from inheriting cloud authentication, MCP servers, plugins, hooks, and ordinary session history.

export CODEX_DS4_HOME="${XDG_STATE_HOME:-$HOME/.local/state}/codex-ds4"
mkdir -p "$CODEX_DS4_HOME/zdot"

cat > "$CODEX_DS4_HOME/config.toml" <<'EOF'
check_for_update_on_startup = false
web_search = "disabled"

[history]
persistence = "none"

[feedback]
enabled = false

[analytics]
enabled = false

[otel]
exporter = "none"
trace_exporter = "none"
metrics_exporter = "none"
log_user_prompt = false
log_agent_responses = false
log_guardian_assessments = false
EOF

This disables the ordinary history file, but interactive Codex can still keep local thread or rollout state under the dedicated home. If the work is sensitive, protect or remove that directory after reviewing what you need to retain.

6. Add the codex-ds4 launcher

Add this function to your shell configuration. It deliberately starts Codex with a minimal environment, a dedicated state directory, no shared daemon, and a Nono sandbox whose only network exception is localhost port 8000.

codex-ds4() {
  local state="${CODEX_DS4_HOME:-${XDG_STATE_HOME:-$HOME/.local/state}/codex-ds4}"
  local tmp="$state/tmp"
  local codex_bin nono_bin

  codex_bin="$(command -v codex)" || return 1
  nono_bin="$(command -v nono)" || return 1
  mkdir -p "$state/zdot" "$tmp"

  env -i \
    HOME="$HOME" \
    USER="${USER:-user}" \
    LOGNAME="${LOGNAME:-${USER:-user}}" \
    SHELL="${SHELL:-/bin/zsh}" \
    PATH="$PATH" \
    TERM="${TERM:-xterm-256color}" \
    LANG="${LANG:-en_US.UTF-8}" \
    TMPDIR="$tmp" \
    ZDOTDIR="$state/zdot" \
    GIT_CONFIG_GLOBAL=/dev/null \
    GIT_TERMINAL_PROMPT=0 \
    CODEX_HOME="$state" \
    CODEX_SQLITE_HOME="$state" \
    DS4_LOCAL_KEY=local-only \
    NO_PROXY=127.0.0.1,localhost \
    no_proxy=127.0.0.1,localhost \
    "$nono_bin" wrap \
      --profile default \
      --allow "$PWD" \
      --allow "$state" \
      --block-net \
      --open-port 8000 \
      -- "$codex_bin" \
        --no-daemon \
        -c 'model_provider="ds4"' \
        -c 'model_providers.ds4.name="DwarfStar local"' \
        -c 'model_providers.ds4.base_url="http://127.0.0.1:8000/v1"' \
        -c 'model_providers.ds4.env_key="DS4_LOCAL_KEY"' \
        -c 'model_providers.ds4.wire_api="responses"' \
        -c 'model_providers.ds4.requires_openai_auth=false' \
        -c 'model_providers.ds4.supports_websockets=false' \
        -c 'model_context_window=32768' \
        -c 'model_auto_compact_token_limit=24576' \
        -m deepseek-v4-flash \
        "$@"
}

Open a fresh shell, enter a disposable Git repository, and run:

codex-ds4

GIT_CONFIG_GLOBAL=/dev/null keeps personal Git identity and credential helpers out of the agent environment. Commit reviewed work from your normal host shell, or provide a separate sanitized Git config deliberately.

7. Verify the privacy boundary

Do not infer privacy from the word “local.” Check the listener, the provider, and the network policy.

Confirm loopback binding

lsof -nP -iTCP:8000 -sTCP:LISTEN

The listener should be 127.0.0.1:8000, not *:8000 or 0.0.0.0:8000.

Prove local access works while external access fails

nono wrap \
  --profile default \
  --block-net \
  --open-port 8000 \
  -- /bin/sh -c '
    curl -fsS http://127.0.0.1:8000/v1/models >/dev/null || exit 1
    if curl -fsS --max-time 3 https://example.com >/dev/null 2>&1; then
      echo "FAIL: external network was reachable" >&2
      exit 1
    fi
    echo "OK: ds4 reachable; external network blocked"
  '

Confirm isolation

  • The normal ~/.codex directory should not be granted to the Nono process.
  • No OpenAI, Anthropic, GitHub, AWS, or other secret variables should be present after the env -i boundary.
  • The launcher must name wire_api="responses" and requires_openai_auth=false.
  • Codex should report the model as deepseek-v4-flash; the GGUF selected by the server determines the actual weights.

8. Operate it safely

PracticeReason
Start in a disposable repositoryLocal tool-use quality can vary by model and quantization
Review git diff outside the agentSandboxing limits reach; it does not make edits correct
Keep the server on loopbackDwarfStar's placeholder API key is not authentication
Keep context conservativeLarge contexts increase memory use, prefill time, and cache exposure
Avoid tracesDebug traces can persist prompts, outputs, and tool details
Launch from a normal host shellAn already sandboxed parent process cannot grant a wider child sandbox

Troubleshooting

SymptomLikely causeFix
Cannot reach ds4Server stopped or wrong portRun the model check against 127.0.0.1:8000
Codex asks for sign-inShared config or provider override not appliedCheck CODEX_HOME, requires_openai_auth=false, and the launcher quoting
Immediate context failureClient limit exceeds server allocationUse the same context value on both sides and compact below it
Very slow V4.1 generationExperts are streaming from SSDUse resident ds4f-q2 for interactive coding, or larger/distributed hardware
Shell tool misses personal aliasesThe launcher intentionally skips shell startup filesUse explicit commands or a reviewed, dedicated tool path

Primary references