refactor: agents/ moves to ~/Projekte/ai-config
.zshrc sources agent-env.zsh from there; README points to the new home.
History of the moved files stays here up to e000cfe.
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
e000cfe121
commit
450b58c2a5
10 changed files with 5 additions and 648 deletions
2
.zshrc
2
.zshrc
|
|
@ -41,4 +41,4 @@ bindkey '^[[B' down-line-or-beginning-search
|
||||||
bindkey '^[OA' up-line-or-beginning-search
|
bindkey '^[OA' up-line-or-beginning-search
|
||||||
bindkey '^[OB' down-line-or-beginning-search
|
bindkey '^[OB' down-line-or-beginning-search
|
||||||
|
|
||||||
source "${0:a:h}/agents/agent-env.zsh"
|
source "$HOME/Projekte/ai-config/agents/agent-env.zsh"
|
||||||
|
|
|
||||||
11
README.md
11
README.md
|
|
@ -6,9 +6,6 @@ Shared shell and tool configuration.
|
||||||
|
|
||||||
Tracked files:
|
Tracked files:
|
||||||
- `.zshrc` — shared zsh UI behavior
|
- `.zshrc` — shared zsh UI behavior
|
||||||
- `agents/AGENTS.md` — shared global personal preferences (agent-agnostic)
|
|
||||||
- `agents/AGENTS-claude.md` — Claude Code wrapper (`@AGENTS.md` + `@~/.claude/RTK.md`)
|
|
||||||
- `agents/AGENTS-opencode.md` — OpenCode wrapper (`@AGENTS.md`)
|
|
||||||
- `claude/statusline-command.sh` — Claude Code statusline (dir, branch, model, thinking, rate limits)
|
- `claude/statusline-command.sh` — Claude Code statusline (dir, branch, model, thinking, rate limits)
|
||||||
|
|
||||||
This repo is intentionally small. Machine-specific setup stays local unless explicitly promoted into shared config later.
|
This repo is intentionally small. Machine-specific setup stays local unless explicitly promoted into shared config later.
|
||||||
|
|
@ -21,15 +18,15 @@ This repo is intentionally small. Machine-specific setup stays local unless expl
|
||||||
source "$HOME/Projekte/dotfiles/.zshrc"
|
source "$HOME/Projekte/dotfiles/.zshrc"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Claude Code & OpenCode**: symlink the wrappers into each agent's config dir:
|
**Claude Code**: statusline symlink:
|
||||||
|
|
||||||
```zsh
|
```zsh
|
||||||
ln -s "$HOME/Projekte/dotfiles/agents/AGENTS-claude.md" ~/.claude/CLAUDE.md
|
|
||||||
ln -s "$HOME/Projekte/dotfiles/agents/AGENTS-opencode.md" ~/.config/opencode/AGENTS.md
|
|
||||||
ln -s "$HOME/Projekte/dotfiles/claude/statusline-command.sh" ~/.claude/statusline-command.sh
|
ln -s "$HOME/Projekte/dotfiles/claude/statusline-command.sh" ~/.claude/statusline-command.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
The wrappers `@`-include the shared base `agents/AGENTS.md`; add agent-specific content directly in the wrapper file.
|
Agent instructions, hooks, `.env.agents` and the agent SSH helpers moved to
|
||||||
|
`~/Projekte/ai-config/agents/` on 2026-10-09 (`.zshrc` sources `agent-env.zsh` from
|
||||||
|
there); see that folder's README for the wiring.
|
||||||
|
|
||||||
## What Lives Here
|
## What Lives Here
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
||||||
# Python schreibt sofort statt gepuffert: lange Läufe im Hintergrund lassen sich
|
|
||||||
# zwischendurch im Log mitlesen (sonst bleibt die Datei bis zum Ende leer)
|
|
||||||
export PYTHONUNBUFFERED=1
|
|
||||||
|
|
||||||
export USE_AGENT_SSH_USER=1
|
|
||||||
# fester Socket des geteilten Agent-ssh-agents (Start + Key-Load macht
|
|
||||||
# agent-env in ~/.zshrc) — hier gesetzt, damit Signing (GIT_CONFIG_* unten)
|
|
||||||
# und ssh auch dann auf dem Ein-Key-Agent landen, wenn eine Shell noch eine
|
|
||||||
# alte agent-env-Funktion ohne Socket-Export geladen hat
|
|
||||||
export SSH_AUTH_SOCK=/Users/mwiegand/.ssh/isac-agent.sock
|
|
||||||
export BW_SSH_ARGS="-o User=mwiegand-agent -o IdentitiesOnly=yes -i /Users/mwiegand/.ssh/id_mwiegand_agent.pub -o IdentityAgent=/Users/mwiegand/.ssh/isac-agent.sock"
|
|
||||||
export BW_SCP_ARGS="$BW_SSH_ARGS"
|
|
||||||
|
|
||||||
# Git-Signing in Agent-Sessions: derselbe Signing-Key wie interaktiv
|
|
||||||
# ("SSH Signing Key mwiegand"), über den geteilten Maschinen-Agent
|
|
||||||
# (isac-agent.sock via SSH_AUTH_SOCK). Signatur = "kam von meiner Maschine";
|
|
||||||
# wer committet hat, steht im Co-Authored-By-Trailer. GIT_CONFIG_* schlägt
|
|
||||||
# lokale und globale git-Configs — Overrides in Repos greifen hier nicht.
|
|
||||||
export GIT_CONFIG_COUNT=2
|
|
||||||
export GIT_CONFIG_KEY_0="user.signingkey"
|
|
||||||
export GIT_CONFIG_VALUE_0="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOxBXKEVpYdw4XBPKEzMDWJqvCy7YyogBaoAo2RnGXQ4"
|
|
||||||
export GIT_CONFIG_KEY_1="gpg.ssh.program"
|
|
||||||
export GIT_CONFIG_VALUE_1="ssh-keygen"
|
|
||||||
|
|
||||||
# influxdb
|
|
||||||
export INFLUXDB_READ_TOKEN="op://24ylu7osc22gtdcoic6rgjsp2m/mdrtosjm42btdrhejoo4eg4mmu/password"
|
|
||||||
export INFLUXDB_URL="op://24ylu7osc22gtdcoic6rgjsp2m/mdrtosjm42btdrhejoo4eg4mmu/url"
|
|
||||||
export INFLUXDB_ORG="op://24ylu7osc22gtdcoic6rgjsp2m/mdrtosjm42btdrhejoo4eg4mmu/org"
|
|
||||||
# bigquery
|
|
||||||
export BIGQUERY_SERVICE_ACCOUNT_JSON="op://24ylu7osc22gtdcoic6rgjsp2m/bjyiugpg4gzruvskxjita6eutm/martin-wiegand-ai-50f35be6b554.json"
|
|
||||||
export BIGQUERY_PROJECT_ID="op://24ylu7osc22gtdcoic6rgjsp2m/bjyiugpg4gzruvskxjita6eutm/project"
|
|
||||||
# agents guide
|
|
||||||
export AGENTS_GUID_TOKEN="op://24ylu7osc22gtdcoic6rgjsp2m/qzwjz73g2evbot5xqqnqtc7tiy/password"
|
|
||||||
# icinga
|
|
||||||
export ICINGA_BASE_URL="op://24ylu7osc22gtdcoic6rgjsp2m/stoktfjxvifslqwguhrpmprcwe/url"
|
|
||||||
export ICINGA_USER="op://24ylu7osc22gtdcoic6rgjsp2m/stoktfjxvifslqwguhrpmprcwe/username"
|
|
||||||
export ICINGA_API_PASSWORD="op://24ylu7osc22gtdcoic6rgjsp2m/stoktfjxvifslqwguhrpmprcwe/password"
|
|
||||||
# grafana
|
|
||||||
export GRAFANA_URL="op://24ylu7osc22gtdcoic6rgjsp2m/jwgq6zd77be4kwvk76q7lskc6i/url"
|
|
||||||
export GRAFANA_READ_TOKEN="op://24ylu7osc22gtdcoic6rgjsp2m/jwgq6zd77be4kwvk76q7lskc6i/password"
|
|
||||||
# ionos
|
|
||||||
export IONOS_API_TOKEN="op://24ylu7osc22gtdcoic6rgjsp2m/c67ygbr54xsxblxgnoljeawbse/password"
|
|
||||||
# swift
|
|
||||||
export SWIFT3_TOKEN="op://24ylu7osc22gtdcoic6rgjsp2m/uigw4g52gkd6ocmzeeqi4fk2wu/password"
|
|
||||||
# bitbucket dc
|
|
||||||
export BITBUCKET_DC_URL="op://24ylu7osc22gtdcoic6rgjsp2m/wipkwb7pmc2xyuq5vibywhqvpi/hostname"
|
|
||||||
export BITBUCKET_DC_PAT="op://24ylu7osc22gtdcoic6rgjsp2m/wipkwb7pmc2xyuq5vibywhqvpi/credential"
|
|
||||||
# bamboo
|
|
||||||
export BAMBOO_DC_URL="op://24ylu7osc22gtdcoic6rgjsp2m/delniouzyvkndwtkmovv7hhauy/hostname"
|
|
||||||
export BAMBOO_DC_PAT="op://24ylu7osc22gtdcoic6rgjsp2m/delniouzyvkndwtkmovv7hhauy/credential"
|
|
||||||
# atlassian cloud
|
|
||||||
export ATLASSIAN_CLOUD_PAT="op://24ylu7osc22gtdcoic6rgjsp2m/ywanqqqcviwqyoeoxa6g2au5ki/credential"
|
|
||||||
export JIRA_READONLY_PAT="op://24ylu7osc22gtdcoic6rgjsp2m/qqi25p2cuwkjkhbxj7spbjq2fe/credential"
|
|
||||||
# n8n prod
|
|
||||||
# "mcp": {
|
|
||||||
# "n8n-seibert": {
|
|
||||||
# "type": "local",
|
|
||||||
# "command": [
|
|
||||||
# "npx",
|
|
||||||
# "-y",
|
|
||||||
# "n8n-mcp"
|
|
||||||
# ],
|
|
||||||
# "environment": {
|
|
||||||
# "N8N_API_KEY": "{env:N8N_SEIBERT_API_KEY}",
|
|
||||||
# "N8N_API_URL": "{env:N8N_SEIBERT_API_URL}"
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
export N8N_SEIBERT_API_URL="op://24ylu7osc22gtdcoic6rgjsp2m/qbkhrul2lm7uxy4kjqjpt5eoju/url"
|
|
||||||
export N8N_SEIBERT_API_KEY="op://24ylu7osc22gtdcoic6rgjsp2m/qbkhrul2lm7uxy4kjqjpt5eoju/credential"
|
|
||||||
# forgejo test token
|
|
||||||
export FORGEJO_URL="op://24ylu7osc22gtdcoic6rgjsp2m/zdkxvnabf23xjnp3lwti43hmma/hostname"
|
|
||||||
export FORGEJO_TOKEN="op://24ylu7osc22gtdcoic6rgjsp2m/zdkxvnabf23xjnp3lwti43hmma/credential"
|
|
||||||
|
|
||||||
# proxmox
|
|
||||||
export PROXMOX_URL_1="op://24ylu7osc22gtdcoic6rgjsp2m/xqpmcgcvytlmptfwwpn7kui6ei/url"
|
|
||||||
export PROXMOX_USER_1="op://24ylu7osc22gtdcoic6rgjsp2m/xqpmcgcvytlmptfwwpn7kui6ei/username"
|
|
||||||
export PROXMOX_TOKEN_1="op://24ylu7osc22gtdcoic6rgjsp2m/xqpmcgcvytlmptfwwpn7kui6ei/credential"
|
|
||||||
|
|
||||||
export PROXMOX_URL_2="op://24ylu7osc22gtdcoic6rgjsp2m/hiax7cmpeezanve2orhstjwchi/url"
|
|
||||||
export PROXMOX_USER_2="op://24ylu7osc22gtdcoic6rgjsp2m/hiax7cmpeezanve2orhstjwchi/username"
|
|
||||||
export PROXMOX_TOKEN_2="op://24ylu7osc22gtdcoic6rgjsp2m/hiax7cmpeezanve2orhstjwchi/credential"
|
|
||||||
|
|
||||||
# ai
|
|
||||||
export VLLM_API_KEY="op://24ylu7osc22gtdcoic6rgjsp2m/ryeegxxtakfolgqqgnwtdio2ny/credential"
|
|
||||||
export GOOGLE_AI_STUDIO_API_KEY="op://24ylu7osc22gtdcoic6rgjsp2m/e225knzbmb6rxe5utr2csjphqu/credential"
|
|
||||||
export CLAUDE_VERTEX_GATEWAY_TOKEN="op://24ylu7osc22gtdcoic6rgjsp2m/gwkninymrh2a7msvsou24c3zvu/credential"
|
|
||||||
export ONYX_ADMIN_TOKEN="op://24ylu7osc22gtdcoic6rgjsp2m/rw6ccrvzirrls47g4vkjurhzm4/credential"
|
|
||||||
|
|
||||||
# elewom
|
|
||||||
export ELEWOM_AUTH_TOKEN="op://24ylu7osc22gtdcoic6rgjsp2m/zwznfforzil5yrjrl77uuo67pi/credential"
|
|
||||||
|
|
||||||
# verda
|
|
||||||
export VERDA_INFERENCE_KEY="op://24ylu7osc22gtdcoic6rgjsp2m/sxnfz2logzqe47d4yilfh5whly/credential"
|
|
||||||
|
|
||||||
# seatable
|
|
||||||
export SEATABLE_ACCOUNT_TOKEN="op://24ylu7osc22gtdcoic6rgjsp2m/smfdkpo4ve7pfeabnoddls72hu/credential"
|
|
||||||
|
|
||||||
# bitbucket cloud seibert media workspace
|
|
||||||
export BITBUCKET_CLOUD_SEIBERT_WORKSPACE="op://24ylu7osc22gtdcoic6rgjsp2m/6vtqs6biiegyhi2z5i24u4nav4/credential"
|
|
||||||
|
|
||||||
# netbox
|
|
||||||
export NETBOX_AUTH_TOKEN="op://24ylu7osc22gtdcoic6rgjsp2m/5gzmvpudiuv6tdzi6amzsbkwcu/credential"
|
|
||||||
|
|
||||||
# sentry
|
|
||||||
export SENTRY_READ_TOKEN="op://24ylu7osc22gtdcoic6rgjsp2m/v3vxfekfbkenbz7hxaqjai4q4m/credential"
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
@./AGENTS.md
|
|
||||||
|
|
||||||
## Company knowledge (onyx & thicket)
|
|
||||||
|
|
||||||
- Company-knowledge questions go to the `onyx` skill first (its description
|
|
||||||
says when); its index surfaces pages that direct CQL/JQL misses. Always cite
|
|
||||||
source links.
|
|
||||||
- Two shortcuts past onyx: Thicket MCP for people lookups (role, team,
|
|
||||||
email — its other sections are thin), Confluence CQL for precise
|
|
||||||
lookups with known space/title.
|
|
||||||
- Same-topic follow-ups: use `onyx answer "..." --session <last session_id>`.
|
|
||||||
|
|
||||||
## Subagents
|
|
||||||
|
|
||||||
**Default to delegating whenever the expected tool output exceeds the
|
|
||||||
conclusion the main thread actually needs** — multi-file research, log/data
|
|
||||||
analysis, doc review, broad searches, verification runs. The session context
|
|
||||||
is the scarce resource; a subagent burns its own context and returns only the
|
|
||||||
result. Work inline only for single-fact lookups where file and symbol are
|
|
||||||
already known — there, a subagent is pure overhead.
|
|
||||||
|
|
||||||
Use the `Agent` tool. Prefer specialized agent types over `general-purpose`
|
|
||||||
when one fits:
|
|
||||||
|
|
||||||
- **`Explore`** — read-only search: *locates* code and facts (reads excerpts,
|
|
||||||
not whole files). It does not review or audit; quality judgments stay with
|
|
||||||
the parent or a dedicated reviewer.
|
|
||||||
- **`Plan`** — implementation strategy, architecture trade-offs.
|
|
||||||
- **`fork`** (`subagent_type: "fork"`) — inherits the full conversation
|
|
||||||
context, runs in the background, keeps its tool output out of the main
|
|
||||||
thread. The tool of choice for "do X with everything you already know, give
|
|
||||||
me only the outcome" — no need to re-explain state in the prompt.
|
|
||||||
- Agent types not listed as available in the session (e.g. a `code-reviewer`)
|
|
||||||
don't exist there — check the available-types list instead of guessing.
|
|
||||||
|
|
||||||
### Behavioral tier (MANDATORY — always set)
|
|
||||||
|
|
||||||
Prefix every `Agent` prompt with one of:
|
|
||||||
|
|
||||||
- `[TIER: FAST]` — return raw results immediately; no internal monologue, no synthesis
|
|
||||||
- `[TIER: STANDARD]` — focused implementation logic; minimal preamble
|
|
||||||
- `[TIER: DEEP]` — complex reasoning; must conclude with a summary
|
|
||||||
|
|
||||||
### Model selection (relative to the parent, no fixed names)
|
|
||||||
|
|
||||||
Resolution order: explicit `model` param → agent type frontmatter → parent
|
|
||||||
inheritance. (`fork` always runs on the parent model; an override is ignored.)
|
|
||||||
Model names shift (Opus was the top tier when this was written; Fable and
|
|
||||||
Mythos sit above it now), so think in tiers, not names:
|
|
||||||
|
|
||||||
0. **Self-hosted backend (a SessionStart hook says so when `ANTHROPIC_BASE_URL`
|
|
||||||
points at the vLLM gateway): never pass `model`.** Aliases do not exist
|
|
||||||
there; every subagent inherits the one available model.
|
|
||||||
1. **Specialized agent types** (`Explore`, `Plan`, …) keep their frontmatter
|
|
||||||
model. Leave `model` off.
|
|
||||||
2. **Routine `general-purpose` work** (mechanical edits, bulk lookups,
|
|
||||||
verification runs): one tier below the parent, today `sonnet`. Don't pay
|
|
||||||
top rates for routine work.
|
|
||||||
3. **Judgement work** (research, recommendation, trade-off, review, blinded
|
|
||||||
eval judges): the parent's tier, so pass no `model` and let it inherit.
|
|
||||||
For "how should X look" questions discuss options first, do not build.
|
|
||||||
4. **`haiku`** only for purely mechanical tasks: bulk classification, format
|
|
||||||
conversion, summarizing pre-filtered text. Code-semantic work is at least
|
|
||||||
`sonnet`.
|
|
||||||
|
|
||||||
### Context hygiene
|
|
||||||
|
|
||||||
- Prefer parallel `Explore` calls over sequential `general-purpose` sessions;
|
|
||||||
spawn independent agents in a single message so they run concurrently.
|
|
||||||
- Once delegated, don't duplicate: never run the same search yourself while
|
|
||||||
an agent is on it, and don't poll — results arrive as notifications.
|
|
||||||
- Follow-ups to an existing agent go via `SendMessage` (keeps its context);
|
|
||||||
a new `Agent` call always starts fresh — right for unrelated lookups.
|
|
||||||
- Summarize subagent findings in the main thread; the raw report is not
|
|
||||||
shown to the user, so relay what matters.
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
@./AGENTS.md
|
|
||||||
194
agents/AGENTS.md
194
agents/AGENTS.md
|
|
@ -1,194 +0,0 @@
|
||||||
# Personal preferences
|
|
||||||
|
|
||||||
## Workflow
|
|
||||||
|
|
||||||
- **Specs, plans, and design docs go into the repo they describe.** When
|
|
||||||
brainstorming, plan-mode, or any flow produces a persistent document
|
|
||||||
(design spec, implementation plan, ADR, etc.), the final destination is
|
|
||||||
always the working tree of the project it belongs to — typically
|
|
||||||
under `docs/` (or wherever the project conventionally keeps them).
|
|
||||||
Scratch locations like `~/.claude/plans/<slug>.md` are fine *during*
|
|
||||||
plan mode, but the moment plan mode exits with an approved artifact,
|
|
||||||
copy it into the repo and `git commit` it. Don't ship a feature
|
|
||||||
while its spec or plan still lives only in a scratch file. If a
|
|
||||||
project's `AGENTS.md` / `CLAUDE.md` specifies a different directory,
|
|
||||||
that wins.
|
|
||||||
|
|
||||||
## Identity
|
|
||||||
|
|
||||||
- Email martin.wiegand@seibert.group, Bitbucket username `mwiegand`.
|
|
||||||
|
|
||||||
## Write actions: confirm before executing
|
|
||||||
|
|
||||||
Before any action that creates, modifies or sends something outside this
|
|
||||||
machine (Jira comments, ticket transitions, Confluence edits, chat messages,
|
|
||||||
emails), show exactly what will happen (the text, the target, the transition)
|
|
||||||
and wait for explicit approval. For bulk operations present the full list
|
|
||||||
first and change nothing until the plan is confirmed. Local edits, commits,
|
|
||||||
pushes and memory writes do not need this.
|
|
||||||
|
|
||||||
## External communication (messages, emails, comments)
|
|
||||||
|
|
||||||
- Neutral and technical: the agent conveys information, it does not imitate
|
|
||||||
a human. No social emojis, no emotional filler ("Danke dir! 🙂", "Huhu").
|
|
||||||
- Structure: factual salutation ("Hallo <Name>,"), precise content, concrete
|
|
||||||
call to action, closing without emotional markers.
|
|
||||||
- Append "(ai generated)" to every external text I phrase; never in chat
|
|
||||||
replies to me, and not in automated webhook or alert texts.
|
|
||||||
- PR thread replies carry only the decisive argument: no side findings,
|
|
||||||
links or follow-up ideas.
|
|
||||||
- Times in chat in Europe/Berlin local time; convert server UTC first.
|
|
||||||
|
|
||||||
## Error handling
|
|
||||||
|
|
||||||
If a tool call fails or hits a rate limit, pause and tell me. One retry with
|
|
||||||
the identified cause fixed (e.g. a sandbox denial) is fine; never retry in a
|
|
||||||
loop or brute-force through API calls.
|
|
||||||
|
|
||||||
## Git commits
|
|
||||||
|
|
||||||
- Conventional Commits; imperative, intent-focused subjects; body only when
|
|
||||||
needed; split unrelated changes; ask only for mixed diffs.
|
|
||||||
- On a Jira issue, prefix the subject with its key (`IT-12345`) in every repo.
|
|
||||||
The ISAC repo has its own single-line convention (`IT-12345 bw/<bundle>:
|
|
||||||
<what>`, no Conventional Commits); the `isac` skill has the details.
|
|
||||||
- **Push and open PRs (also drafts) without asking**, then report the link.
|
|
||||||
Not covered: force-push, pushes to shared or foreign branches, merges, PR
|
|
||||||
comments. Exception: in the private l4d2 project Martin pushes himself.
|
|
||||||
- **Never `--amend` and never `git stash pop` when other sessions may work in
|
|
||||||
the same repo.** An amend rewrote another session's pushed commit once
|
|
||||||
(2026-09-25); a pop after "No local changes to save" pops a foreign stash,
|
|
||||||
in the ISAC repo one with staged secrets. Fix-ups are new commits; to look
|
|
||||||
at an old version use `git show <ref>:<path>`.
|
|
||||||
- **Commit messages via `-F` use one absolute path for writing and reading**
|
|
||||||
(the Bash tool's cwd changes silently; a relative `.tmp/commitmsg.txt` once
|
|
||||||
landed in a task folder and the commit took the old message). Check
|
|
||||||
`head -1` before committing; several commits per turn get numbered files.
|
|
||||||
- **Own PR branches: readable over historical.** Re-cut commits into one per
|
|
||||||
logical step (pure `git mv` first, content second) and push with
|
|
||||||
`--force-with-lease`; rebuild from the final tree instead of amending.
|
|
||||||
Bitbucket detects renames only by content similarity, so keep a renamed
|
|
||||||
file small.
|
|
||||||
- `git push` over SSH fails inside the sandbox (no raw TCP; `excludedCommands`
|
|
||||||
does not help): push with `dangerouslyDisableSandbox: true`, ISAC via
|
|
||||||
`isac git -C <repo>`.
|
|
||||||
|
|
||||||
## Jira
|
|
||||||
|
|
||||||
- Search with JQL scoped to issues, never Confluence. Default filter:
|
|
||||||
`assignee = currentUser() AND status NOT IN (Done, Closed) ORDER BY duedate ASC`
|
|
||||||
- Asked to work on a ticket: present the ticket, then a numbered action plan.
|
|
||||||
No vague follow-up questions.
|
|
||||||
- Write actions (tickets, comments) go through the `jira-write` skill.
|
|
||||||
Comments are internal by default; public only when they address the
|
|
||||||
participants, never silently public.
|
|
||||||
|
|
||||||
## Code health
|
|
||||||
|
|
||||||
The `coding` skill carries the rules (act on cleanups, scope boundary, rule of
|
|
||||||
three, friction as architectural data, per-language style). Load it before
|
|
||||||
editing code if it did not trigger on its own.
|
|
||||||
|
|
||||||
## Interpreting requests
|
|
||||||
|
|
||||||
Treat vague input and question-back responses critically, not as instructions.
|
|
||||||
|
|
||||||
- **Vagueness is not authorization.** "Make it cleaner", "maybe X?",
|
|
||||||
"looks good but…" are signals to ask one clarifying question, not green
|
|
||||||
lights to pick an interpretation and run.
|
|
||||||
- **A tangent is not an answer.** If you ask "A or B?" and get "maybe X?",
|
|
||||||
that's a new question, not a pick. Surface the mismatch and ask which the
|
|
||||||
user meant — don't bundle the tangent into the active task as if it were
|
|
||||||
direction.
|
|
||||||
|
|
||||||
## Team memory (cognee)
|
|
||||||
|
|
||||||
cognee is a real memory, not an archive: unlike `MEMORY.md` it is **not**
|
|
||||||
injected automatically, so it only works if you fetch from it and write to it
|
|
||||||
on your own. Command forms, flags, dataset sharing and failure modes are in the
|
|
||||||
`cognee` plugin skill; this section only says *when*.
|
|
||||||
|
|
||||||
- **Read: `cognee recall <question>` before the first tool call** when the task
|
|
||||||
touches infrastructure, a server, a service, a ticket or a tool we run
|
|
||||||
ourselves; when an error "smells familiar"; when choosing between options
|
|
||||||
where a decision may already exist; or when the task will plausibly take more
|
|
||||||
than a handful of tool calls. Plain `recall` reads every readable dataset,
|
|
||||||
team included. Treat what comes back like a colleague's note: useful, possibly
|
|
||||||
outdated; verify a named file, flag or host still exists before acting on it.
|
|
||||||
- **Write: `cognee remember` as the insight appears**, not at session end.
|
|
||||||
Announce it in one line, never ask first: memory writes are exempt from any
|
|
||||||
project rule that asks to confirm write actions (those rules target external
|
|
||||||
systems: Jira, chat, Confluence, hosts). Moments: a bug fixed whose cause was
|
|
||||||
not visible in the code; a decision with a rejected alternative (save the
|
|
||||||
*why*); a measurement that contradicts an assumption; a correction, yours or
|
|
||||||
mine; a tool or API that behaves differently than its docs claim.
|
|
||||||
- **Routing:** `--team` for everything concerning team IT (Seibert IT-Infra,
|
|
||||||
ISAC/bw, monitoring, INFRA board, shared services, team processes), plain
|
|
||||||
`remember` for my preferences and personal context. Never secrets,
|
|
||||||
credentials or customer-personal data; nothing the repo already records;
|
|
||||||
nothing that only matters inside this conversation.
|
|
||||||
- **Projects with their own findings convention** (task folders,
|
|
||||||
`erkenntnisse/`, `decisions/`): write the file there first, then the cognee
|
|
||||||
note with the conclusion and a link to the file. The file is the detailed
|
|
||||||
record; cognee is what the next session or a colleague in another repo finds.
|
|
||||||
- **Hygiene:** on recurring topics `recall` first and extend the existing note
|
|
||||||
instead of adding a second one. Write notes that answer a question: concrete
|
|
||||||
numbers, the error message verbatim, the fix, and the external references
|
|
||||||
(Confluence, Jira, TeamVault, Google Chat thread URLs
|
|
||||||
`https://chat.google.com/room/<SPACE-ID>/<THREAD-ID>`). No size limit, so
|
|
||||||
write self-contained.
|
|
||||||
|
|
||||||
## Sandbox
|
|
||||||
|
|
||||||
- **Always run commands sandboxed first.** Only use `dangerouslyDisableSandbox: true` after a sandbox-related failure — never preemptively. One targeted retry with the identified cause fixed is fine; retrying in a loop is not.
|
|
||||||
- **On sandbox failure:** analyze the error message for the blocked path, then suggest adding it to `sandbox.filesystem.allowWrite` in `~/.claude/settings.json`. Only fall back to `dangerouslyDisableSandbox: true` if the path can't be determined or the user prefers it.
|
|
||||||
- **Commands that must run unsandboxed belong in `sandbox.excludedCommands`** (e.g. `git *`, the pinned
|
|
||||||
`bin/srv`-style wrappers), not behind the flag: the exclusion is the owner's decision, the flag is the
|
|
||||||
model's. Without a hook, the flag is only a convention: an unsandboxed command that matches an allow
|
|
||||||
rule (`Bash(git *)`, `Bash(pytest *)` …) runs without a prompt in default mode, and auto mode may wave it
|
|
||||||
through as well. (The former PreToolUse hook that forced a prompt on the flag was removed 2026-10-04;
|
|
||||||
its JSON is in this file's git history.)
|
|
||||||
|
|
||||||
## Temporary Files
|
|
||||||
|
|
||||||
- **Use `$TMPDIR` or `.tmp/` (project-local) for temporary files.** Never use `/tmp` directly or paths like `/Library/Application Support` — those are not in the sandbox allowlist and trigger permission prompts.
|
|
||||||
- `$TMPDIR` is set by the sandbox to a writable path. `.tmp/` inside the project directory is always writable without prompts.
|
|
||||||
- Ensure `.tmp/` is listed in `.gitignore` when creating temp files in a tracked repo.
|
|
||||||
|
|
||||||
## Shell — literal `!` in Bash commands
|
|
||||||
|
|
||||||
- **Never put a literal `!` in an inline Bash command.** Claude Code's Bash tool
|
|
||||||
escapes every `!` to `\!` at the transport layer before any shell sees it —
|
|
||||||
even inside single/double quotes and in non-interactive shells
|
|
||||||
(anthropics/claude-code#61121, a regression of a fix shipped in 2.1.87). The
|
|
||||||
stray backslash corrupts downstream tools: Python `!=` → SyntaxWarning + broken
|
|
||||||
string, jq, Jira JQL (`status != Done` → server 400), branch names, etc.
|
|
||||||
`bash -c '...'` and quoted heredocs do **not** avoid it.
|
|
||||||
- **One rule covers everything:** if a command needs a `!`, write the command/code
|
|
||||||
to a file with the Write tool under `.tmp/` and run the file. `.tmp/` is
|
|
||||||
prompt-free via the `Edit(.tmp/**)` allow rule in `~/.claude/settings.json`. Do
|
|
||||||
**not** create the file with inline `printf`/`echo >` — that re-escapes the `!`.
|
|
||||||
- Need a literal `!` inline anyway: ANSI-C hex `$'\x21'` (e.g. `B=$'\x21'; cmd
|
|
||||||
"${B}=…"`) yields a real `!` with no literal `!` in the command.
|
|
||||||
|
|
||||||
## Tooling
|
|
||||||
|
|
||||||
- **Sourcebot for Seibert repos without a local checkout:** the `sourcebot` skill
|
|
||||||
(MCP tools, gotchas such as mirrors and result caps; never `ask_codebase` on
|
|
||||||
your own). A local checkout stays first choice.
|
|
||||||
- **`ccc` before `grep` for concept questions.** In a repo with a
|
|
||||||
`.cocoindex_code/` directory, a question about a concept ("where is X
|
|
||||||
handled", "what talks to Y", "which code does Z") starts with
|
|
||||||
`ccc search '<concept>'` from the project root, then `grep` verifies what it
|
|
||||||
found. An exact string (UUID, ticket ID, error message, identifier) goes
|
|
||||||
straight to `grep`/`rg`: ccc is embeddings only and will not find it. Gotchas
|
|
||||||
in the `ccc` skill.
|
|
||||||
- **`ccc index` / `ccc init` are read-safe maintenance.** They only write to the
|
|
||||||
gitignored `.cocoindex_code/` directory, never to source. Run them without
|
|
||||||
confirmation prompts; treat like `grep` for permission purposes.
|
|
||||||
- **`md` for long Markdown files instead of `head`/`grep -n '^#'`.**
|
|
||||||
`md outline <file>` prints the heading tree with line numbers and size per
|
|
||||||
section; decide from that, then `md section <file> "<heading>"` for exactly
|
|
||||||
that subtree (`--range` yields `offset limit` for the Read tool, `--depth 0`
|
|
||||||
cuts subsections). It knows code fences and frontmatter, so a `# comment` in
|
|
||||||
a bash block is not a heading. Below ~500 lines just `Read` the file.
|
|
||||||
185
agents/README.md
185
agents/README.md
|
|
@ -1,185 +0,0 @@
|
||||||
# Design: SSH-Key- und Git-Signing-Architektur (Arbeitsrechner mwiegand)
|
|
||||||
|
|
||||||
Stand 2026-08-04 — umgesetzt und verifiziert. Beschreibt das *Warum* hinter
|
|
||||||
dem Code in diesem Verzeichnis (`agent-env.zsh`, `ensure-machine-agent`,
|
|
||||||
`.env.agents`) plus `~/.ssh/config` und `~/.gitconfig`.
|
|
||||||
|
|
||||||
Umsetzungsstand, Historie und die offenen Schritte auf ISAC-Seite stehen im
|
|
||||||
ai-Repo: `tasks/2026-07-31-agent-key-isolation/HANDOFF.md`, Vorarbeit in
|
|
||||||
`tasks/2026-06-13-isac-infra-agent-isolation/`.
|
|
||||||
|
|
||||||
## Problem
|
|
||||||
|
|
||||||
Auf dem Rechner laufen zwei Arten von Akteuren mit unterschiedlichem
|
|
||||||
Vertrauensniveau:
|
|
||||||
|
|
||||||
- **Ich** (interaktive Shell) — brauche den persönlichen SSH-Key, der über
|
|
||||||
LDAP auch auf **Kundensystemen** autorisiert ist.
|
|
||||||
- **Agents** (Claude/OpenCode via `agent-env`) — sollen interne Systeme
|
|
||||||
erreichen, aber **strukturell nicht unbemerkt** an den kundenfähigen Key
|
|
||||||
kommen.
|
|
||||||
|
|
||||||
Vor dem Umbau war die Trennung rein konventioniert: der private Haupt-Key lag
|
|
||||||
unverschlüsselt als `~/.ssh/id_mwiegand` auf der Platte (für jede Agent-Session
|
|
||||||
lesbar), der 1Password-SSH-Agent bot alle Keys jedem Prozess an, und ein
|
|
||||||
Login-Script lud den Haupt-Key zusätzlich in den Apple-Agent. Git-Signing hing
|
|
||||||
am Datei-Key.
|
|
||||||
|
|
||||||
Dazu die Komfort-Anforderung: **Signieren darf im Alltag nicht nerven** —
|
|
||||||
keine Fingerabdruck-Freigabe pro Commit.
|
|
||||||
|
|
||||||
## Anforderungen
|
|
||||||
|
|
||||||
1. Agents verwenden einen eigenen SSH-Key (nur interne Systeme); der
|
|
||||||
kundenfähige Haupt-Key ist für sie nicht still nutzbar.
|
|
||||||
2. Jede Nutzung des Haupt-Keys erzeugt eine sichtbare, bewusste Freigabe
|
|
||||||
(Fingerabdruck) — der Prompt ist die Schranke.
|
|
||||||
3. Kein privater Schlüssel liegt als Datei auf der Platte.
|
|
||||||
4. Git-Commits sind signiert, prompt-frei — Herkunft „diese Maschine" ist
|
|
||||||
nachweisbar (Mensch/Agent unterscheiden die Commit-Trailer).
|
|
||||||
5. Kein Autoload beim Login (explizite Entscheidung): was in einem Agent
|
|
||||||
liegt, wurde bewusst dorthin gelegt.
|
|
||||||
|
|
||||||
## Architektur: drei Schlüssel, drei Wege
|
|
||||||
|
|
||||||
| Schlüssel (1Password-Item, Vault Employee) | Fingerprint | Lebt in | Zweck | Prompt-Verhalten |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| **SSH Key mwiegand** (Haupt-Key) | `SHA256:lIx1Na…` | nur 1Password | SSH überall, inkl. Kundensysteme | 1Password-Freigabe pro Key und Terminal-Session — bewusst spürbar |
|
|
||||||
| **SSH isac agent** (Agent-Key) | `SHA256:77FiJM…` | geteilter Maschinen-ssh-agent `~/.ssh/isac-agent.sock` | SSH auf interne Systeme (User `mwiegand-agent`) | einmal `op read` pro Boot, danach still |
|
|
||||||
| **SSH Signing Key mwiegand** (Signing-Key) | `SHA256:SvHSJQ…` | geteilter Maschinen-ssh-agent `~/.ssh/isac-agent.sock` | Git-Signing **aller** Commits der Maschine — meine und Agent-Commits | einmal `ensure-machine-agent` pro Boot (passiert automatisch mit der ersten Agent-Session), danach still |
|
|
||||||
|
|
||||||
Die Auswahl des Schlüssels erfolgt **über den Agent-Socket**, nicht über
|
|
||||||
Key-Dateien:
|
|
||||||
|
|
||||||
- `~/.ssh/config` `Host *` → `IdentityAgent ~/.1password/agent.sock`
|
|
||||||
(Haupt-Key, promptet pro Nutzung).
|
|
||||||
- `Match exec USE_AGENT_SSH_USER=1` → `User mwiegand-agent` +
|
|
||||||
`IdentityAgent ~/.ssh/isac-agent.sock` (Ein-Key-Agent; steht vor `Host *`,
|
|
||||||
nach den ckn-Host-Blöcken).
|
|
||||||
- Git-Signing läuft über `SSH_AUTH_SOCK`, der überall (interaktive Shell via
|
|
||||||
`agent-env.zsh`, Agent-Sessions zusätzlich via `.env.agents`) auf den
|
|
||||||
Maschinen-Agent zeigt — signiert wird immer mit dem Signing-Key. Fürs
|
|
||||||
interaktive SSH ist `SSH_AUTH_SOCK` irrelevant, weil die `ssh_config`
|
|
||||||
`IdentityAgent` explizit setzt.
|
|
||||||
|
|
||||||
Der Sicherheitskern: **die Prompt-Semantik bleibt sauber.** Ein
|
|
||||||
1Password-Prompt bedeutet immer „jemand will den kundenfähigen Key" — er
|
|
||||||
kommt selten und ist ernst zu nehmen. Signing und Agent-SSH erzeugen nie
|
|
||||||
Prompts, also gibt es keine Gewöhnung an reflexhaftes Bestätigen.
|
|
||||||
|
|
||||||
## Komponenten
|
|
||||||
|
|
||||||
Alles Agent-bezogene liegt versioniert in diesem Verzeichnis; `~/.zshrc` und
|
|
||||||
`~/.local/bin` enthalten nur noch Verweise darauf.
|
|
||||||
|
|
||||||
| Datei | Rolle |
|
|
||||||
|---|---|
|
|
||||||
| `agents/agent-env.zsh` (gesourced von `dotfiles/.zshrc`) | exportiert `SSH_AUTH_SOCK` fest auf `~/.ssh/isac-agent.sock`; `agent-env()` ruft `ensure-machine-agent` und dann `op run --env-file ~/.env.agents` |
|
|
||||||
| `agents/ensure-machine-agent` (Symlink in `~/.local/bin/`) | idempotent: startet den Maschinen-Agent bei Bedarf und stellt **beide** Keys sicher (Signing + Agent). Wird von `agent-env` gerufen; manuell nur nötig, wenn man nach einem Reboot committen will, bevor die erste Agent-Session lief |
|
|
||||||
| `agents/.env.agents` (Symlink `~/.env.agents`) | rein deklarativ — `op run` parst die Datei nur, führt keinen Code aus: `USE_AGENT_SSH_USER=1`, `SSH_AUTH_SOCK` (Robustheit: Konsumenten ohne `agent-env` finden den richtigen Socket), `BW_SSH_ARGS`, `GIT_CONFIG_*` → `user.signingkey` = Signing-Pubkey (Env schlägt alle git-Configs, auch lokale), Secret-Referenzen |
|
|
||||||
| `~/.ssh/config` | Socket-Routing s. o.; keine `IdentityFile`-Einträge für persönliche Keys |
|
|
||||||
| `~/.gitconfig` | `gpg.format=ssh`, `commit.gpgsign=true`, `user.signingkey` = Signing-Pubkey-String (kein Pfad), `gpg.ssh.allowedSignersFile` |
|
|
||||||
| `~/.ssh/allowed_signers` | beide Pubkeys (Signing + Agent) für lokale Verifikation (`git log --format=%G?`) |
|
|
||||||
|
|
||||||
Gelöscht: `~/.ssh/id_mwiegand` (unverschlüsselter Datei-Key),
|
|
||||||
`add-ssh-key-to-agent` (Login-Autoload des Haupt-Keys), lokale
|
|
||||||
`user.signingkey`-Overrides in 8 Repos.
|
|
||||||
|
|
||||||
## Entscheidungen und verworfene Alternativen
|
|
||||||
|
|
||||||
**Signing-Key als eigener Schlüssel statt Haupt-Key.** Die Prompt-Frequenz
|
|
||||||
hängt nicht am Schlüssel, sondern am Mechanismus — aber ein gemeinsamer
|
|
||||||
Schlüssel hätte Haupt-Key-Prompts beim Committen alltäglich gemacht und die
|
|
||||||
Schranke durch Gewöhnung entwertet. Außerdem: Forges kennen den Signing-Key
|
|
||||||
nur als Signaturschlüssel; ein Leak ist kein Systemzugriff.
|
|
||||||
|
|
||||||
**Ein gemeinsamer Signing-Key für Mensch und Agents** (statt Agent-Commits
|
|
||||||
mit dem Agent-SSH-Key zu signieren, wie zuerst gebaut). Die Key-Trennung
|
|
||||||
hätte nur *ehrliche* Agent-Commits von meinen unterschieden: die
|
|
||||||
Agent-Sockets sind für jeden lokalen Prozess erreichbar, ein böswilliger
|
|
||||||
Agent könnte ohnehin als ich signieren. Für ehrliche Commits leistet der
|
|
||||||
`Co-Authored-By`-Trailer die Unterscheidung. Die Signatur bedeutet ehrlich
|
|
||||||
„kam von meiner Maschine"; dafür halbiert sich die Forge-Pflege (ein Key
|
|
||||||
registrieren/verifizieren statt zwei) und der Boot-Load läuft automatisch
|
|
||||||
über die erste Agent-Session mit. Ältere Commits (2026-08-04) sind noch mit
|
|
||||||
dem Agent-Key signiert — er bleibt dafür in `allowed_signers`.
|
|
||||||
|
|
||||||
**`op-ssh-sign` erwogen und verworfen.** Zuerst umgesetzt (Key verlässt
|
|
||||||
1Password nie), dann zurückgebaut: die 1Password-Autorisierung gilt global
|
|
||||||
pro App(+Terminal-Session) für *alle* Keys. Das Signing-Nerv-Problem hätte
|
|
||||||
sich nur durch Lockern des Modells lösen lassen — was die Haupt-Key-Schranke
|
|
||||||
mitgelockert hätte. Der Agent-Weg entkoppelt beides: Signing prompt-frei,
|
|
||||||
1Password-Modell bleibt streng. (Zwischenschritt Apple-Agent als
|
|
||||||
Signing-Halter wurde durch den Maschinen-Agent ersetzt — ein Agent weniger.)
|
|
||||||
|
|
||||||
**macOS-Keychain als Signer verworfen.** Die Keychain speichert Bytes, kann
|
|
||||||
aber keine SSH-Signaturen erzeugen (`security` bietet nur `cms`). Jeder
|
|
||||||
Keychain-Weg endet in einer Klartext-Tempdatei pro Commit — schlechter als
|
|
||||||
jeder Agent. (`ssh-add --apple-use-keychain` speichert nur Passphrasen und
|
|
||||||
setzt eine Key-Datei voraus.)
|
|
||||||
|
|
||||||
**Kein Login-Autoload.** Bewusste Entscheidung: Keys werden nicht beim Login
|
|
||||||
geladen, sondern lazy — mit der ersten Agent-Session (automatisch) bzw. per
|
|
||||||
`ensure-machine-agent` von Hand. Symptom eines fehlenden Loads ist ein
|
|
||||||
Commit-Fehler `Couldn't find key in agent?` — eindeutig und folgenlos.
|
|
||||||
|
|
||||||
**Geteilter Agent statt Agent pro Session.** Ein fester Socket
|
|
||||||
(`~/.ssh/isac-agent.sock`) statt mktemp pro Session: ein `op read` pro Key
|
|
||||||
und Boot statt pro Session, kein Cleanup verwaister Agents, und der
|
|
||||||
Match-Block kann auf einen stabilen Pfad zeigen (wirkt auch für Subprozesse
|
|
||||||
mit bereinigtem Env). Beide Bewohner (Agent-Key, Signing-Key) sind per
|
|
||||||
Definition die Schlüssel, die Agents zugänglich sein dürfen — Langlebigkeit
|
|
||||||
weicht nichts auf.
|
|
||||||
|
|
||||||
**`SSH_AUTH_SOCK` zusätzlich fest in `.env.agents`.** Lektion aus dem Umbau:
|
|
||||||
eine Shell mit veralteter `agent-env`-Funktion lud die neue `.env.agents`
|
|
||||||
(Signing-Config auf Agent-Key), zeigte aber noch auf den leeren Apple-Agent →
|
|
||||||
`Couldn't find key in agent?`. Signing-Config und Socket dürfen nicht
|
|
||||||
auseinanderfallen; beide stehen jetzt in derselben Datei.
|
|
||||||
|
|
||||||
## Grenzen (bewusst offen)
|
|
||||||
|
|
||||||
Die Architektur ist eine **Zustimmungs- und Erkennungsschranke, keine
|
|
||||||
technische Unerreichbarkeit**:
|
|
||||||
|
|
||||||
- Der 1Password-Socket bleibt für Agent-Prozesse erreichbar
|
|
||||||
(`allowAllUnixSockets: true` in der Claude-Sandbox-Config; 1Password kann
|
|
||||||
Keys nicht pro Client filtern). Ein Agent, der den Haupt-Key anfordert,
|
|
||||||
löst aber einen sichtbaren Prompt aus, den ich ablehnen kann.
|
|
||||||
- **`op`-CLI-Pfad** (empirisch geprüft 2026-08-04): ein Agent kann `op read`
|
|
||||||
auf beliebige Items *versuchen* — u. a. den Haupt-Key — aber nicht
|
|
||||||
unbemerkt. Vier Versuche aus einer Agent-Session ergaben vier
|
|
||||||
1Password-Dialoge; die Freigabe für das `op run` beim `agent-env`-Start
|
|
||||||
deckt nachgelagerte `op`-Aufrufe **nicht** mit ab (ob eine *bestätigte*
|
|
||||||
Freigabe danach ein 10-Minuten-Fenster öffnet, ist unverifiziert — der
|
|
||||||
Dialog behauptet das, gemessen wurde es nicht).
|
|
||||||
Die verbleibende Schwäche ist die **Blindheit des Dialogs**: er sagt „full
|
|
||||||
access to your account", nennt aber nicht das angefragte Item. Beim
|
|
||||||
Bestätigen ist nicht unterscheidbar, ob ein Agent ein harmloses
|
|
||||||
API-Passwort oder den kundenfähigen SSH-Key liest. Das ist systematisch,
|
|
||||||
nicht nur eine Eigenschaft offener Abfragen: auch `op read` mit einer
|
|
||||||
punktgenauen `op://Employee/<item>/<feld>`-Referenz zeigt denselben
|
|
||||||
Dialog (geprüft 2026-08-04) — 1Password autorisiert das Werkzeug, nie den
|
|
||||||
Zugriff, selbst wenn dieser vollständig spezifiziert ist. Gerade die
|
|
||||||
Secrets in `.env.agents` sind ausschließlich solche Referenzen — und weil legitime
|
|
||||||
Agent-Starts denselben Dialog erzeugen, trainiert der Alltag reflexhaftes
|
|
||||||
Bestätigen. Genau das macht der gescopte Service-Account (Schritt 1)
|
|
||||||
überflüssig: was nicht anfragbar ist, muss nicht beurteilt werden.
|
|
||||||
- Vollständige Schließung = HANDOFF-Schritte 1/3: gescopter Service-Account
|
|
||||||
mit eigenem Vault für den Agent-Key, Socket-Sperre in der Sandbox,
|
|
||||||
Seatbelt-Variante für unsandboxte Kommandos.
|
|
||||||
|
|
||||||
## Betrieb
|
|
||||||
|
|
||||||
- **Nach Reboot:** die erste `agent-env`-Session lädt beide Keys automatisch;
|
|
||||||
nur wer vorher committen will, ruft einmal `ensure-machine-agent` (Symptom
|
|
||||||
sonst: `Couldn't find key in agent?`).
|
|
||||||
- **Verifikation:** `git log --format='%h %G? %GK'` — `G` + `SvHSJQ…` für
|
|
||||||
alle Maschinen-Commits (Commits vom 2026-08-04 noch mit `77FiJM…`).
|
|
||||||
`ssh-add -l` in einer Agent-Session zeigt genau die zwei Maschinen-Keys.
|
|
||||||
- **Forges:** der Signing-Pubkey muss bei Bitbucket DC/Forgejo als *Signing*
|
|
||||||
Key registriert sein; bei Forgejo zusätzlich ownership-verifiziert
|
|
||||||
(Challenge signieren, nur via Web-UI), sonst „unverified" (offen).
|
|
||||||
- **Rotation:** neuer Key = neues 1Password-Item + Pubkey in
|
|
||||||
`~/.gitconfig`/`allowed_signers`/`.env.agents` bzw. ISAC
|
|
||||||
(`admin_agent_users`) nachziehen; es gibt keine Key-Dateien zu tauschen.
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
# Agent-Sessions (Claude, OpenCode): Secrets aus 1Password und SSH-Identität
|
|
||||||
# des Agent-Users. Wird von dotfiles/.zshrc gesourced.
|
|
||||||
# Architektur und Begründungen: agents/README.md.
|
|
||||||
|
|
||||||
# Git-Signing und ssh-add greifen über SSH_AUTH_SOCK auf den geteilten
|
|
||||||
# Maschinen-Agent zu — interaktiv wie in Agent-Sessions. Fürs interaktive SSH
|
|
||||||
# ist die Variable irrelevant, weil ~/.ssh/config IdentityAgent explizit setzt
|
|
||||||
# (Haupt-Key via 1Password, Agent-User via diesem Socket).
|
|
||||||
export SSH_AUTH_SOCK="$HOME/.ssh/isac-agent.sock"
|
|
||||||
|
|
||||||
agent-env() {
|
|
||||||
# Agent + beide Maschinen-Keys sicherstellen; der op-read darin teilt sich
|
|
||||||
# die 1Password-Autorisierung mit dem op-run unten, also ein Prompt.
|
|
||||||
ensure-machine-agent || return 1
|
|
||||||
|
|
||||||
op run --account WHZOJQOJPZHTFPJC6JZAOCUCBI \
|
|
||||||
--env-file ~/.env.agents --no-masking -- "$@"
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# SessionStart hook: tell the agent when the session runs against the self-hosted
|
|
||||||
# vLLM gateway, where model aliases (sonnet, opus, haiku) do not exist.
|
|
||||||
# Prints nothing on Anthropic/Vertex so the default rules apply.
|
|
||||||
case "${ANTHROPIC_BASE_URL:-}" in
|
|
||||||
*vllm*|*seibert.tools*)
|
|
||||||
model="${ANTHROPIC_MODEL:-${CLAUDE_CODE_SUBAGENT_MODEL:-the configured model}}"
|
|
||||||
printf '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"Self-hosted backend (%s): only %s is available. Never pass a model override to Agent calls; all subagents inherit this model."}}\n' "$ANTHROPIC_BASE_URL" "$model"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
exit 0
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# Stellt den geteilten Maschinen-ssh-agent sicher: Socket läuft und hält beide
|
|
||||||
# Keys, die auf dieser Maschine gebraucht werden. Idempotent — mehrfach
|
|
||||||
# aufrufbar, macht nur was fehlt.
|
|
||||||
#
|
|
||||||
# SSH isac agent SSH auf interne Systeme (User mwiegand-agent)
|
|
||||||
# SSH Signing Key Git-Signing, für mich UND Agent-Sessions
|
|
||||||
#
|
|
||||||
# Wird von agent-env bei jedem Start gerufen; manuell nur nötig, wenn man nach
|
|
||||||
# einem Reboot committen will, bevor die erste Agent-Session lief (Symptom:
|
|
||||||
# "error: Couldn't find key in agent?").
|
|
||||||
#
|
|
||||||
# Der kundenfähige Haupt-Key ("SSH Key mwiegand") gehört bewusst NICHT hierher:
|
|
||||||
# er lebt nur hinter der 1Password-Freigabe (~/.1password/agent.sock, siehe
|
|
||||||
# Host * in ~/.ssh/config), damit Agents ihn nicht unbemerkt nutzen können.
|
|
||||||
# Architektur und Begründungen: agents/README.md im dotfiles-Repo.
|
|
||||||
|
|
||||||
PATH="/opt/homebrew/bin:/usr/bin:/bin:$PATH"
|
|
||||||
SOCK="$HOME/.ssh/isac-agent.sock"
|
|
||||||
OP_ACCOUNT="WHZOJQOJPZHTFPJC6JZAOCUCBI"
|
|
||||||
export SSH_AUTH_SOCK="$SOCK"
|
|
||||||
|
|
||||||
# Agent tot oder nie gestartet? (ssh-add exit 2 = Socket antwortet nicht)
|
|
||||||
ssh-add -l >/dev/null 2>&1
|
|
||||||
if [ $? -eq 2 ]; then
|
|
||||||
rm -f "$SOCK"
|
|
||||||
/usr/bin/ssh-agent -a "$SOCK" >/dev/null || exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# $1 = Fingerprint (schon geladen?), $2 = op-Referenz, $3 = Anzeigename
|
|
||||||
ensure_key() {
|
|
||||||
if ssh-add -l 2>/dev/null | grep -q "$1"; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
if op read --account "$OP_ACCOUNT" "$2" | ssh-add -q -; then
|
|
||||||
echo "$3 geladen (bis zum nächsten Reboot)." >&2
|
|
||||||
else
|
|
||||||
echo "$3 konnte nicht geladen werden (1Password gesperrt?)" >&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
rc=0
|
|
||||||
ensure_key "SHA256:SvHSJQcHlAWUmF8EnflNsrv0mhWGARvAKLRPSBQ460I" \
|
|
||||||
"op://Employee/37z2tbpilrjrgyrhckb5cbrhsq/private key?ssh-format=openssh" \
|
|
||||||
"Signing-Key" || rc=1
|
|
||||||
ensure_key "SHA256:77FiJM6Z1eN7Ka7YpoCKIn9xVJFQUj6nBQa/g1rFya4" \
|
|
||||||
"op://Employee/ec2mtn4kkxnypyc4gxutg7lajq/private key?ssh-format=openssh" \
|
|
||||||
"Agent-Key" || rc=1
|
|
||||||
exit $rc
|
|
||||||
Loading…
Reference in a new issue