dotfiles

andersuno
git clone git://git.andersuno.nu/dotfiles.git
Log | Files | Refs | README

opencode.jsonc (14407B)


      1 {
      2   "$schema": "https://opencode.ai/config.json",
      3 
      4   "providers": {
      5     "ollama": {
      6       "name": "Ollama Local",
      7       "package": "aisdk:@ai-sdk/openai-compatible",
      8       "settings": {
      9         "baseURL": "http://127.0.0.1:11434/v1"
     10       },
     11       "models": {
     12         "gemma4:e2b": {
     13           "name": "gemma4:e2b",
     14           "capabilities": {
     15             "tools": true,
     16             "input": ["text", "image"],
     17             "output": ["text"]
     18           }
     19         }
     20       }
     21     }
     22   },
     23 
     24   "mcp": {
     25     "servers": {
     26       "gitlab": {
     27         "type": "remote",
     28         "url": "https://gitlab.com/api/v4/mcp",
     29         "disabled": true
     30       },
     31       "brewfather": {
     32         "type": "local",
     33         "command": ["uv", "run", "brewfather-mcp"],
     34         "cwd": "/home/anders/Repos/brewfather-mcp",
     35         "disabled": true,
     36         "timeout": {
     37           "catalog": 30000,
     38           "execution": 30000
     39         }
     40       }
     41     }
     42   },
     43 
     44   // oh-my-opencode
     45   "lsp": true,
     46   "plugins": [
     47     "oh-my-opencode-slim"
     48   ],
     49   "agents": {
     50     "explore": {
     51       "disabled": true
     52     },
     53     "general": {
     54       "disabled": true
     55     },
     56   // oh-my-opencode
     57 
     58     "title": {
     59       "model": "opencode/big-pickle"
     60     },
     61     "slim-shady": {
     62       "description": "Slim prompt agent for general questions",
     63       "mode": "primary",
     64       "system": "You are a helpful assistant",
     65       "permissions": [
     66         { "action": "*", "resource": "*", "effect": "deny" }
     67       ]
     68     }
     69   },
     70 
     71   "websearch": {
     72     "provider": "random",
     73   },
     74   "formatter": true,
     75 
     76   // V2: ordered rules, last matching rule wins (broad first, exceptions after)
     77   "permissions": [
     78     // Read-only tools: ask for anything, but never touch .env / .envrc
     79     { "action": "read", "resource": "*", "effect": "ask" },
     80     { "action": "read", "resource": "*.env", "effect": "deny" },
     81     { "action": "read", "resource": "*.envrc", "effect": "deny" },
     82     { "action": "read", "resource": "*cloud.conf", "effect": "deny" },
     83     { "action": "read", "resource": "*secret*", "effect": "deny" },
     84     { "action": "read", "resource": "*terraform.tfstate*", "effect": "deny" },
     85 
     86     // glob/grep resources are the query, not a path, so path-shaped denies do nothing here
     87     { "action": "glob", "resource": "*", "effect": "ask" },
     88     { "action": "grep", "resource": "*", "effect": "ask" },
     89 
     90     { "action": "subagent", "resource": "*", "effect": "allow" },
     91     { "action": "skill", "resource": "*", "effect": "allow" },
     92     { "action": "question", "resource": "*", "effect": "allow" },
     93     { "action": "webfetch", "resource": "*", "effect": "allow" },
     94     { "action": "websearch", "resource": "*", "effect": "allow" },
     95 
     96     { "action": "browser", "resource": "*", "effect": "deny" },
     97 
     98     { "action": "edit", "resource": "*", "effect": "ask" },
     99 
    100     // shell (was "bash" in V1)
    101     { "action": "shell", "resource": "*", "effect": "ask" },
    102     // basic inspection
    103     { "action": "shell", "resource": "echo *", "effect": "ask" },
    104     { "action": "shell", "resource": "ls*", "effect": "allow" },
    105     { "action": "shell", "resource": "pwd*", "effect": "allow" },
    106     { "action": "shell", "resource": "cat*", "effect": "allow" },
    107     { "action": "shell", "resource": "head*", "effect": "allow" },
    108     { "action": "shell", "resource": "tail*", "effect": "allow" },
    109     { "action": "shell", "resource": "find*", "effect": "allow" },
    110     { "action": "shell", "resource": "grep*", "effect": "allow" },
    111     { "action": "shell", "resource": "rg*", "effect": "allow" },
    112     { "action": "shell", "resource": "diff*", "effect": "allow" },
    113     { "action": "shell", "resource": "file*", "effect": "allow" },
    114     { "action": "shell", "resource": "which*", "effect": "allow" },
    115     { "action": "shell", "resource": "command -v *", "effect": "allow" },
    116     { "action": "shell", "resource": "wc*", "effect": "allow" },
    117     { "action": "shell", "resource": "jq*", "effect": "allow" },
    118     { "action": "shell", "resource": "sort*", "effect": "allow" },
    119     { "action": "shell", "resource": "uniq*", "effect": "allow" },
    120     { "action": "shell", "resource": "du*", "effect": "allow" },
    121     { "action": "shell", "resource": "df*", "effect": "allow" },
    122     { "action": "shell", "resource": "uname*", "effect": "allow" },
    123     { "action": "shell", "resource": "sleep *", "effect": "allow" },
    124     // read-only git
    125     { "action": "shell", "resource": "git status*", "effect": "allow" },
    126     { "action": "shell", "resource": "git diff*", "effect": "allow" },
    127     { "action": "shell", "resource": "git ls-files*", "effect": "allow" },
    128     { "action": "shell", "resource": "git log*", "effect": "allow" },
    129     { "action": "shell", "resource": "git grep*", "effect": "allow" },
    130     { "action": "shell", "resource": "git show*", "effect": "allow" },
    131     // git branch/remote: read-only forms only — no -D/-f/-m/--delete, no add/remove/set-url
    132     { "action": "shell", "resource": "git branch", "effect": "allow" },
    133     { "action": "shell", "resource": "git branch --list*", "effect": "allow" },
    134     { "action": "shell", "resource": "git branch -a", "effect": "allow" },
    135     { "action": "shell", "resource": "git branch -r", "effect": "allow" },
    136     { "action": "shell", "resource": "git branch -v", "effect": "allow" },
    137     { "action": "shell", "resource": "git branch -vv", "effect": "allow" },
    138     { "action": "shell", "resource": "git branch -av", "effect": "allow" },
    139     { "action": "shell", "resource": "git remote", "effect": "allow" },
    140     { "action": "shell", "resource": "git remote -v", "effect": "allow" },
    141     { "action": "shell", "resource": "git remote get-url*", "effect": "allow" },
    142     { "action": "shell", "resource": "git stash list*", "effect": "allow" },
    143     // read-only go
    144     { "action": "shell", "resource": "go version*", "effect": "allow" },
    145     { "action": "shell", "resource": "go env*", "effect": "allow" },
    146     // test / check commands
    147     { "action": "shell", "resource": "npm test*", "effect": "allow" },
    148     { "action": "shell", "resource": "npm run lint*", "effect": "allow" },
    149     { "action": "shell", "resource": "npm run typecheck*", "effect": "allow" },
    150     { "action": "shell", "resource": "cargo check*", "effect": "allow" },
    151     { "action": "shell", "resource": "cargo test*", "effect": "allow" },
    152     { "action": "shell", "resource": "go vet*", "effect": "allow" },
    153     { "action": "shell", "resource": "go test*", "effect": "allow" },
    154     { "action": "shell", "resource": "pytest*", "effect": "allow" },
    155     { "action": "shell", "resource": "python3 -m pytest*", "effect": "allow" },
    156     // mutating: always confirm (after the matching allows above)
    157     { "action": "shell", "resource": "git add*", "effect": "ask" },
    158     { "action": "shell", "resource": "git commit*", "effect": "ask" },
    159     { "action": "shell", "resource": "git push*", "effect": "ask" },
    160     // network + writes .git
    161     { "action": "shell", "resource": "git fetch*", "effect": "ask" },
    162     { "action": "shell", "resource": "git reset*", "effect": "ask" },
    163     { "action": "shell", "resource": "git checkout*", "effect": "ask" },
    164     { "action": "shell", "resource": "git clean*", "effect": "ask" },
    165     { "action": "shell", "resource": "git rebase*", "effect": "ask" },
    166     { "action": "shell", "resource": "npm install*", "effect": "ask" },
    167     { "action": "shell", "resource": "npm run build*", "effect": "ask" },
    168     { "action": "shell", "resource": "cargo build*", "effect": "ask" },
    169     { "action": "shell", "resource": "go build*", "effect": "ask" },
    170     // go env -w/-u write the persistent go env config (after the go env allow above)
    171     { "action": "shell", "resource": "go env -w*", "effect": "ask" },
    172     { "action": "shell", "resource": "go env -u*", "effect": "ask" },
    173     { "action": "shell", "resource": "pip install*", "effect": "ask" },
    174     { "action": "shell", "resource": "terraform plan*", "effect": "ask" },
    175     { "action": "shell", "resource": "terraform apply*", "effect": "ask" },
    176     { "action": "shell", "resource": "docker*", "effect": "ask" },
    177     { "action": "shell", "resource": "curl*", "effect": "ask" },
    178     { "action": "shell", "resource": "wget*", "effect": "ask" },
    179     { "action": "shell", "resource": "sudo*", "effect": "ask" },
    180     { "action": "shell", "resource": "chmod*", "effect": "ask" },
    181     { "action": "shell", "resource": "chown*", "effect": "ask" },
    182     { "action": "shell", "resource": "cp*", "effect": "ask" },
    183     { "action": "shell", "resource": "mv*", "effect": "ask" },
    184     { "action": "shell", "resource": "mkdir*", "effect": "ask" },
    185     { "action": "shell", "resource": "touch*", "effect": "ask" },
    186     { "action": "shell", "resource": "rm *", "effect": "ask" },
    187     // environment $VAR
    188     { "action": "shell", "resource": "printenv *", "effect": "deny" },
    189     { "action": "shell", "resource": "env *", "effect": "deny" },
    190     // .env / .envrc — content-dumping readers only; absorb * covers quoted and path-prefixed forms
    191     { "action": "shell", "resource": "cat* *.env*", "effect": "deny" },
    192     { "action": "shell", "resource": "head* *.env*", "effect": "deny" },
    193     { "action": "shell", "resource": "tail* *.env*", "effect": "deny" },
    194     { "action": "shell", "resource": "diff* *.env*", "effect": "deny" },
    195     { "action": "shell", "resource": "find* *.env*", "effect": "deny" },
    196     { "action": "shell", "resource": "git diff* *.env*", "effect": "deny" },
    197     { "action": "shell", "resource": "git show* *.env*", "effect": "deny" },
    198     { "action": "shell", "resource": "git log* *.env*", "effect": "deny" },
    199     // grep/rg: absorb would also block "process.env" searches, so bare + path forms separately
    200     { "action": "shell", "resource": "grep* .env*", "effect": "deny" },
    201     { "action": "shell", "resource": "grep* */.env*", "effect": "deny" },
    202     { "action": "shell", "resource": "rg* .env*", "effect": "deny" },
    203     { "action": "shell", "resource": "rg* */.env*", "effect": "deny" },
    204     // .env.example exceptions
    205     { "action": "shell", "resource": "* .env.example", "effect": "allow" },
    206     // terraform state — same readers; no dot-prefix, so one absorb pattern each
    207     { "action": "shell", "resource": "cat* *.tfstate*", "effect": "deny" },
    208     { "action": "shell", "resource": "head* *.tfstate*", "effect": "deny" },
    209     { "action": "shell", "resource": "tail* *.tfstate*", "effect": "deny" },
    210     { "action": "shell", "resource": "diff* *.tfstate*", "effect": "deny" },
    211     { "action": "shell", "resource": "find* *.tfstate*", "effect": "deny" },
    212     { "action": "shell", "resource": "git diff* *.tfstate*", "effect": "deny" },
    213     { "action": "shell", "resource": "git show* *.tfstate*", "effect": "deny" },
    214     { "action": "shell", "resource": "git log* *.tfstate*", "effect": "deny" },
    215     { "action": "shell", "resource": "grep* *.tfstate*", "effect": "deny" },
    216     { "action": "shell", "resource": "rg* *.tfstate*", "effect": "deny" },
    217     // Sops
    218     { "action": "shell", "resource": "sops*", "effect": "deny" },
    219     // read-only flags that exec a helper or write files
    220     { "action": "shell", "resource": "git* --output*", "effect": "deny" },
    221     { "action": "shell", "resource": "rg* --pre *", "effect": "deny" },
    222     { "action": "shell", "resource": "rg* --pre=*", "effect": "deny" },
    223     { "action": "shell", "resource": "sort* -o *", "effect": "deny" },
    224     // destructive: never auto-run (after the broader asks above)
    225     { "action": "shell", "resource": "rm -rf*", "effect": "deny" },
    226     { "action": "shell", "resource": "rm -fr*", "effect": "deny" },
    227     { "action": "shell", "resource": "git push --force*", "effect": "deny" },
    228     { "action": "shell", "resource": "shutdown*", "effect": "deny" },
    229     { "action": "shell", "resource": "reboot*", "effect": "deny" },
    230     { "action": "shell", "resource": "mkfs*", "effect": "deny" },
    231     { "action": "shell", "resource": "dd *", "effect": "deny" },
    232     // rm -rf exceptions
    233     { "action": "shell", "resource": "rm -rf /tmp/opencode/*", "effect": "ask" },
    234     { "action": "shell", "resource": "rm -fr /tmp/opencode/*", "effect": "ask" },
    235 
    236     // Files outside the workspace: confirm, but block secrets outright
    237     { "action": "external_directory", "resource": "*", "effect": "ask" },
    238     { "action": "external_directory", "resource": "/tmp/opencode/*", "effect": "allow" },
    239     { "action": "external_directory", "resource": "~/.config/opencode/skills/*", "effect": "allow" },
    240     // credential stores, API tokens, and shell history (secrets pasted into commands)
    241     { "action": "external_directory", "resource": "~/.aws*", "effect": "deny" },
    242     { "action": "external_directory", "resource": "~/.gnupg*", "effect": "deny" },
    243     { "action": "external_directory", "resource": "~/.kube*", "effect": "deny" },
    244     { "action": "external_directory", "resource": "~/.s3cfg*", "effect": "deny" },
    245     { "action": "external_directory", "resource": "~/.ssh*", "effect": "deny" },
    246     { "action": "external_directory", "resource": "~/.vault_pass.txt*", "effect": "deny" },
    247     { "action": "external_directory", "resource": "~/.vault-token*", "effect": "deny" },
    248     { "action": "external_directory", "resource": "~/.git-credentials*", "effect": "deny" },
    249     { "action": "external_directory", "resource": "~/.netrc*", "effect": "deny" },
    250     { "action": "external_directory", "resource": "~/.npmrc*", "effect": "deny" },
    251     { "action": "external_directory", "resource": "~/.pypirc*", "effect": "deny" },
    252     { "action": "external_directory", "resource": "~/.docker*", "effect": "deny" },
    253     { "action": "external_directory", "resource": "~/.cargo/credentials*", "effect": "deny" },
    254     { "action": "external_directory", "resource": "~/.config/gh*", "effect": "deny" },
    255     { "action": "external_directory", "resource": "~/.config/gcloud*", "effect": "deny" },
    256     { "action": "external_directory", "resource": "~/.config/opencode/auth.json*", "effect": "deny" },
    257     { "action": "external_directory", "resource": "~/.local/share/opencode/auth.json*", "effect": "deny" },
    258     { "action": "external_directory", "resource": "~/.claude/.credentials.json*", "effect": "deny" },
    259     { "action": "external_directory", "resource": "~/.bash_history*", "effect": "deny" },
    260     { "action": "external_directory", "resource": "~/.zsh_history*", "effect": "deny" },
    261     { "action": "external_directory", "resource": "~/.python_history*", "effect": "deny" },
    262     { "action": "external_directory", "resource": "~/.node_repl_history*", "effect": "deny" }
    263   ]
    264 }