Files
nixos-config/home/coding/agents/opencode.nix
m3ta-chiron 6a5d8f0011 feat(agents): add strict security hardening for Pi and OpenCode
Pi Guardrails:
- Enables @aliou/pi-guardrails with strict default config
- Sets onboarding.completed = true to skip onboarding prompt
- Enables pathAccess in ask mode for /nix/store and /tmp
- Adds noAccess policies for: SSH keys, GPG keys, AWS config,
  Kubernetes config, cloud CLI configs (gh/gcloud/1password/sops),
  agenix secrets, Pi auth/sessions, env files, private keys/certs
- Adds auto-deny patterns for env leakage commands:
  env, printenv, /proc/*/environ, GPG secret exports,
  ssh-add -D, password manager reads

OpenCode permissions:
- Adds permission section with global security rules
- external_directory: ask by default, allow /nix/store and /tmp
- read/edit: allow by default, deny SSH/GPG/AWS/Kube/cloud configs,
  agenix secrets, Pi auth/sessions, env files, private keys/certs
- glob: restrict sensitive path patterns
- grep: deny SSH/GPG/agenix, ask for PASSWORD/SECRET/API_KEY/PRIVATE_KEY
- bash: ask by default, allow safe git/nix commands,
  deny env/printenv/proc/GPG secret/sudo/ssh-add deletion/curl|sh
- webfetch: ask by default, allow github/nixos search
- doom_loop: ask
2026-04-29 19:48:29 +02:00

261 lines
7.3 KiB
Nix

{
inputs,
lib,
...
}: {
coding.agents.opencode = {
enable = true;
agentsInput = inputs.agents;
};
coding.opencode = {
enable = true;
ohMyOpencodeSettings = {
agents = {
sisyphus.model = "litellm/claude-opus-4-6";
oracle.model = "litellm/claude-sonnet-4-6";
librarian.model = "litellm/claude-sonnet-4-6";
explore.model = "litellm/claude-haiku-4-5";
multimodal-looker.model = "litellm/gpt-5.3-codex";
prometheus.model = "litellm/claude-opus-4-6";
metis.model = "litellm/claude-opus-4-6";
momus.model = "litellm/claude-opus-4-6";
atlas.model = "litellm/claude-sonnet-4-6";
};
categories = {
visual-engineering.model = "zai-coding-plan/glm-5.1";
ultrabrain.model = "litellm/claude-opus-4-6";
deep.model = "litellm/claude-sonnet-4-6";
artistry.model = "zai-coding-plan/glm-5.1";
quick.model = "litellm/claude-haiku-4-5";
unspecified-low.model = "litellm/claude-sonnet-4-6";
unspecified-high.model = "litellm/claude-opus-4-6";
writing.model = "zai-coding-plan/glm-5.1";
};
};
};
# Keep TUI settings in programs.opencode.tui to satisfy OpenCode v1.2.15+.
programs.opencode.tui.theme = "opencode";
# Override legacy default settings to avoid deprecated TUI keys in settings.
programs.opencode.settings = lib.mkForce {
plugin = ["oh-my-openagent"];
formatter = {
alejandra = {
command = ["alejandra" "-q" "-"];
extensions = [".nix"];
};
};
# Security: permission hardening for OpenCode
# Last matching rule wins. Glob patterns: * = any chars, ? = single char.
# ~ and $HOME are expanded to the user's home directory.
# external_directory gates paths outside the working directory.
permission = {
# External directory access: ask by default, allow safe paths
"external_directory" = {
"*" = "ask";
"/nix/store/**" = "allow";
"/tmp/**" = "allow";
};
# Read access: allow by default, deny sensitive paths
"read" = {
"*" = "allow";
"~/.ssh/**" = "deny";
"~/.gnupg/**" = "deny";
"~/.aws/**" = "deny";
"~/.kube/**" = "deny";
"~/.config/gh/**" = "deny";
"~/.config/gcloud/**" = "deny";
"~/.config/op/**" = "deny";
"~/.config/sops/**" = "deny";
"/run/agenix/**" = "deny";
"~/.pi/agent/auth.json" = "deny";
"~/.pi/agent/sessions/**" = "deny";
"*.env" = "deny";
"*.env.*" = "deny";
"*.pem" = "deny";
"*.key" = "deny";
"*.p12" = "deny";
"*.pfx" = "deny";
"*id_rsa*" = "deny";
"*id_ed25519*" = "deny";
"*id_ecdsa*" = "deny";
"*.example.env" = "allow";
"*.sample.env" = "allow";
"*.test.env" = "allow";
".env.example" = "allow";
".env.sample" = "allow";
".env.test" = "allow";
"~/.ssh/*.pub" = "allow";
"*.pub" = "allow";
"*.csr" = "allow";
};
# Edit access: ask by default, deny sensitive paths
"edit" = {
"*" = "ask";
"~/.ssh/**" = "deny";
"~/.gnupg/**" = "deny";
"~/.aws/**" = "deny";
"~/.kube/**" = "deny";
"~/.config/gh/**" = "deny";
"~/.config/gcloud/**" = "deny";
"~/.config/op/**" = "deny";
"~/.config/sops/**" = "deny";
"/run/agenix/**" = "deny";
"~/.pi/agent/auth.json" = "deny";
"~/.pi/agent/sessions/**" = "deny";
"*.env" = "deny";
"*.env.*" = "deny";
"*.pem" = "deny";
"*.key" = "deny";
"*.p12" = "deny";
"*.pfx" = "deny";
"*id_rsa*" = "deny";
"*id_ed25519*" = "deny";
"*id_ecdsa*" = "deny";
"~/.ssh/*.pub" = "allow";
"*.pub" = "allow";
"*.csr" = "allow";
};
# Glob patterns: same rules as read for file matching
"glob" = {
"*" = "allow";
"~/.ssh/**" = "deny";
"~/.gnupg/**" = "deny";
"/run/agenix/**" = "deny";
"*.env" = "deny";
"*.env.*" = "deny";
"*.pem" = "deny";
"*.key" = "deny";
"*.p12" = "deny";
"*.pfx" = "deny";
};
# Grep: allow search, but deny searching for secrets
"grep" = {
"*" = "allow";
"~/.ssh/**" = "deny";
"~/.gnupg/**" = "deny";
"/run/agenix/**" = "deny";
"*PASSWORD*" = "ask";
"*SECRET*" = "ask";
"*API_KEY*" = "ask";
"*PRIVATE_KEY*" = "ask";
};
# Bash: ask by default, deny dangerous and env-leak commands
"bash" = {
"*" = "ask";
"git status*" = "allow";
"git diff*" = "allow";
"git log*" = "allow";
"git branch*" = "allow";
"git show*" = "allow";
"git remote*" = "allow";
"nix --version" = "allow";
"nix eval*" = "allow";
"nix build*" = "allow";
"nix develop*" = "allow";
"nix shell*" = "allow";
"nix search*" = "allow";
"alejandra*" = "allow";
"git add*" = "allow";
"git commit*" = "allow";
"git push*" = "ask";
"git pull*" = "allow";
"rm *" = "ask";
"rm -rf *" = "deny";
"sudo *" = "ask";
"env" = "deny";
"printenv" = "deny";
"cat /proc/*/environ" = "deny";
"gpg *--export-secret*" = "deny";
"ssh-add -D" = "deny";
"docker run --privileged*" = "deny";
"curl *| *sh" = "deny";
"wget *| *sh" = "deny";
};
# Web fetch: ask for sensitive URLs
"webfetch" = {
"*" = "ask";
"https://api.github.com*" = "allow";
"https://search.nixos.org*" = "allow";
};
# Doom loop guard
"doom_loop" = "ask";
};
# AZ-Gruppe LiteLLM endpoint + available models
provider = {
litellm = {
npm = "@ai-sdk/openai-compatible";
name = "LiteLLM (AZ-Gruppe)";
options.baseURL = "https://llm.az-gruppe.com/v1";
models = {
"gpt-5.2" = {
name = "GPT-5.2";
limit = {
context = 400000;
output = 128000;
};
};
"gpt-5.3-codex" = {
name = "GPT-5.3 Codex";
limit = {
context = 400000;
output = 128000;
};
};
"claude-haiku-4-5" = {
name = "Claude Haiku 4.5";
options = {
thinking = {
type = "enabled";
budget_tokens = 16000;
};
};
limit = {
context = 200000;
output = 64000;
};
};
"claude-sonnet-4-6" = {
name = "Claude Sonnet 4.6";
options = {
thinking = {
type = "enabled";
budget_tokens = 16000;
};
};
limit = {
context = 200000;
output = 64000;
};
};
"claude-opus-4-6" = {
name = "Claude Opus 4.6";
options = {
thinking = {
type = "enabled";
budget_tokens = 16000;
};
};
limit = {
context = 200000;
output = 128000;
};
};
};
};
};
};
}