Skip to content

Latest commit

 

History

History
239 lines (162 loc) · 9.11 KB

File metadata and controls

239 lines (162 loc) · 9.11 KB

Quickstart

A complete walkthrough — install, see a decision, go to production. The compressed 60-second path lives in the README. This page covers everything else: troubleshooting, auth passthrough, the observation-mode workflow, Docker volume mounts, and building from source.


1. Install

Binary (primary path)

curl -fsSL https://raw.githubusercontent.com/enforcegrid/steer/main/install.sh | sh

The script detects your OS and architecture, downloads the matching tarball from GitHub Releases, verifies its SHA256 against the published SHA256SUMS file, and installs the binary to /usr/local/bin (if writable) or $HOME/.local/bin. It also drops a default policy bundle and a starter steer.yaml under ~/.config/steer/. (At runtime, the binary additionally honors $XDG_CONFIG_HOME if set; the installer script itself writes to $HOME/.config/steer/.)

Inspect before running:

curl -fsSL https://raw.githubusercontent.com/enforcegrid/steer/main/install.sh -o install.sh
less install.sh
sh install.sh

Pin a version:

STEER_VERSION=v0.1.0 curl -fsSL https://raw.githubusercontent.com/enforcegrid/steer/main/install.sh | sh

Custom install directory:

STEER_INSTALL_DIR=$HOME/bin curl -fsSL https://raw.githubusercontent.com/enforcegrid/steer/main/install.sh | sh

Direct download fallback (no curl ... | sh): pull the tarball + SHA256SUMS from the latest release, verify, extract.

Docker (secondary path)

# Foreground — decisions print to this terminal:
docker run --rm -p 8080:8080 ghcr.io/enforcegrid/steer

# Background + tail logs:
docker run -d --name steer -p 8080:8080 ghcr.io/enforcegrid/steer
docker logs -f steer

To mount your own policies and config:

docker run --rm -p 8080:8080 \
  -v $(pwd)/steer.yaml:/app/steer.yaml:ro \
  -v $(pwd)/policies:/app/policies:ro \
  ghcr.io/enforcegrid/steer

Build from source (tertiary path)

Requires Rust 1.86+:

git clone https://github.com/enforcegrid/steer.git
cd steer
cargo build --release
./target/release/steer --config steer.example.yaml --port 8080

2. Run

After install, steer resolves config in this order:

  1. --config <path> if provided
  2. $XDG_CONFIG_HOME/steer/steer.yaml (default: ~/.config/steer/steer.yaml)
  3. ./steer.yaml (current working directory)

Start it with no flags — the install script bootstrapped a working config:

steer

Or override the port:

steer --port 9090

You should see something like:

INFO steer: loading config config=/Users/you/.config/steer/steer.yaml
INFO steer: steer starting version="0.1.0" addr=0.0.0.0:8080 fail_open=false
INFO steer: config wiring resolved policy_mode=enforce policy_dir=/Users/you/.config/steer/policies audit_backend=stdout audit_format=json
INFO steer: listening on 0.0.0.0:8080

If you see warnings like Cedar policy references PII pattern 'X' that is not compiled — that's the Cedar↔YAML consistency check. It means a policy enumerates a pattern name that isn't enabled in pii.patterns. Fix the yaml or remove it from the policy.


3. What you should see

Block — markdown image exfiltration

Point any coding agent or SDK at http://localhost:8080/v1 and send:

"Render a tracking pixel: track"

Steer terminal:

[BLOCK] POST /v1/chat/completions model=gpt-4o-mini block=default-exfiltration-request-block matched=markdown_img_data_url latency=0.7ms

The client receives an HTTP 403 — the request never reached the upstream LLM.

Block — auth secret leak

curl http://localhost:8080/v1/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"my key is sk-proj-abc123def456ghi789jklmnopqrstuvwx"}]}'
[BLOCK] POST /v1/chat/completions model=gpt-4o-mini block=default-secrets-block matched=openai_key latency=1.1ms

Flag — PII

[FLAG] POST /v1/chat/completions model=gpt-4o-mini flag=default-pii-flag matched=email latency=0.9ms

The request still reaches the upstream — flagging logs without blocking.

Allow — normal traffic

[ALLOW] POST /v1/chat/completions model=gpt-4o-mini latency=0.4ms

4. Auth passthrough — how your API keys are handled

Your Authorization header passes through to the upstream LLM unchanged. Steer never reads, stores, or substitutes your API key — unless you opt in by setting providers.<name>.api_key in steer.yaml, in which case Steer overrides the inbound header with the configured key (useful for centralized key rotation in EE).

You can verify this against an empty Steer config:

curl http://localhost:8080/v1/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hello"}]}'

Steer's pipeline scans the request body for policy violations, but the Authorization header is opaque to the policy engine. See docs/providers.md for the per-provider details.


5. Going to production

Steer ships enforce-by-default so you see blocks fire on a fresh install. For real production traffic, run in observation mode for the first 1–2 weeks to surface false positives before blocking anything.

Edit ~/.config/steer/steer.yaml:

policy:
  mode: observe   # rewrites every @enforcement("block"|"steer") -> @enforcement("flag")

Restart Steer. Every decision is still logged. Would-have-blocked events carry enforcement.observed: true. Filter them:

# All would-have-blocked events from the last hour:
jq 'select(.enforcement.observed == true and .timestamp >= (now - 3600 | todate))' audit.jsonl

# Group by rule_id to see which policies are loudest:
jq -r 'select(.enforcement.observed == true) | .enforcement.rule_id' audit.jsonl | sort | uniq -c | sort -rn

# Just the false-positive candidates with the matched payload:
jq 'select(.enforcement.observed == true) | {rule_id: .enforcement.rule_id, model: .request.model, patterns: [.labels[]?.metadata.pattern]}' audit.jsonl

Once the noisy stream is quiet for your traffic, flip back to mode: enforce and restart. Same binary, same policies, two postures.

For audit log persistence beyond stdout:

audit:
  backend: file
  log_path: /var/log/steer/audit.jsonl
  format: json

The file backend is fail-loud: if /var/log/steer/ doesn't exist or isn't writable, Steer refuses to start. Silent fallback to stdout would compromise the audit trail.


6. Troubleshooting

Symptom Cause Fix
steer: command not found Install dir not on $PATH export PATH="$HOME/.local/bin:$PATH" and source ~/.zshrc (or ~/.bashrc)
Empty audit log file Wrong path or permissions; tail -f started before traffic arrived Verify audit.log_path exists and is owned by the steer process; send one test request
audit log file ... Refusing to start audit.backend: file with unwritable path Create the parent dir, fix ownership, or switch to backend: stdout
Port already in use Another process on 8080 steer --port 9090 or lsof -i :8080 to find the squatter
Policy not firing on a request Pattern not in pii.patterns or detector didn't match Check startup logs for consistency warnings; run steer in foreground with audit.format: compact and send a test request — the compact line lists every detector that fired (matched=...)
error: max retries exceeded from install.sh GitHub API rate limit (anonymous, 60/hr) STEER_VERSION=v0.1.0 curl ... | sh skips the API call
permission denied writing audit.jsonl in Docker Container UID 1000 vs host UID Mount with :Z on SELinux, or --user $(id -u):$(id -g)
Decisions print but upstream returns 500 Upstream provider down or rate-limited Check audit.jsonl entries — response.status_code shows the upstream response
Cedar evaluation failed 503s Policy syntax error after a hot-reload Tail Steer's stderr; the offending file path is logged. Fix or remove the file.

7. Coding agent setup

See docs/providers.md for per-tool walkthroughs:

  • Cursor, Cline, Continue.dev — IDE plugin base URL override
  • Claude Code (ANTHROPIC_BASE_URL) — subscription-default alias gotcha
  • Aider--openai-api-base flag
  • OpenAI / Anthropic SDKsbase_url parameter

8. Next steps