Shared Directories¶
Overview¶
When a task starts, terok mounts host directories into the container for workspace access and shared agent configuration.
Per-Task Workspace¶
- Host path:
<state_dir>/tasks/<project_id>/<task_id>/workspace-dangerous - Mounted as:
<host_dir>:/workspace:Z - Created automatically when the task runs (permissions
700). - The project repository is cloned or synced here by
init-ssh-and-repo.sh.
Security warning: The container has full write access to this directory and could have rewritten git hooks, checked in malicious scripts, or otherwise poisoned the repository. Do not execute code or run
gitcommands in this directory from the host. The safer way to interact with agent work is through the git gate — a host-controlled bare repo where agents push their changes for human review before promotion to upstream.
Shared Agent Configuration Directories¶
These directories are bind-mounted into every task container so that agents and
tools find their config on startup. They are created automatically
on first task launch. The base dir defaults to
~/.local/share/terok/agent/mounts (override via TEROK_AGENT_STATE_DIR).
Trust boundary: Mount directories are intentionally separated from the credentials store (
~/.local/share/terok/credentials/) since containers have read-write access to mounts and could potentially poison them.
| Host Dir | Container Mount | Purpose |
|---|---|---|
_codex-config |
/home/dev/.codex |
Codex credentials |
_claude-config |
/home/dev/.claude |
Claude Code credentials (CLAUDE_CONFIG_DIR is set) |
_vibe-config |
/home/dev/.vibe |
Mistral Vibe credentials |
_blablador-config |
/home/dev/.blablador |
Blablador credentials (includes isolated OpenCode config via OPENCODE_CONFIG) |
_opencode-config |
/home/dev/.config/opencode |
Plain OpenCode config (use terok config import-opencode) |
_opencode-data |
/home/dev/.local/share/opencode |
OpenCode data/caches (shared by Blablador and plain OpenCode) |
_opencode-state |
/home/dev/.local/state |
OpenCode/Bun state (shared by both) |
_gh-config |
/home/dev/.config/gh |
GitHub CLI config |
_glab-config |
/home/dev/.config/glab-cli |
GitLab CLI config |
All shared dirs use :z (shared SELinux label); the workspace uses :Z (private label).
Note: SSH keys are not mounted into containers. The credential proxy's SSH agent serves keys over TCP — private keys never enter the container. See
terok ssh-initfor key generation.
SSH Key Management¶
SSH keys are generated and stored on the host only — they are served to containers via the credential proxy's SSH agent (TCP-based, phantom-token authenticated). Public HTTPS repos don't need SSH setup at all.
Setup¶
This generates an ed25519 keypair stored at <state_dir>/ssh-keys/<project_id>/
and registers it in ssh-keys.json for the SSH agent proxy.
Use the printed .pub key to register a deploy key on your Git host.
Custom SSH Config Template¶
Supported tokens: {{IDENTITY_FILE}}, {{KEY_NAME}}, {{PROJECT_ID}}
Credential Proxy¶
The credential proxy holds real API keys and OAuth tokens on the host and injects phantom tokens into containers. When a container makes an API call, the proxy intercepts the phantom token and swaps it for the real credential — the actual secret never enters any container.
Per-task vs shared tokens¶
| Auth method | Token scope | Notes |
|---|---|---|
| API key (all providers) | Per-task | Each task gets a unique phantom token that is revoked when the task ends. |
| OAuth (Codex, etc.) | Per-task | Same per-task lifecycle as API keys. |
| Claude OAuth | Shared | Uses a static marker token in .credentials.json instead of a per-task phantom. All tasks share the same lookup key. |
Claude OAuth is shared because Claude Code determines the subscription
plan display ("Claude Max", "Claude Pro", etc.) by reading accessToken
from ~/.claude/.credentials.json. If the token comes from the
CLAUDE_CODE_OAUTH_TOKEN environment variable instead, Claude Code
always shows "Claude API" regardless of subscription metadata. The
static marker in the file works around this limitation.
Implications of shared Claude OAuth:
- The credential applies to all tasks in the default credential set — you cannot have per-project or per-task Claude OAuth credentials.
- The static marker token is not revoked between tasks. The real OAuth token is still protected by the proxy and refreshed automatically.
- API key auth for Claude (
terok auth claude→ option 2) remains per-task and is unaffected.
Git Identity¶
terok configures git author/committer identities to distinguish AI-generated commits. The mapping is controlled by git.authorship in project.yml or global config.yml:
| Mode | Author | Committer |
|---|---|---|
agent-human (default) |
AI agent | Human |
human-agent |
Human | AI agent |
human |
Human | Human |
agent |
AI agent | AI agent |
Each agent wrapper supplies its own AI identity (e.g. Claude <noreply@anthropic.com>, Codex <noreply@openai.com>, Mistral Vibe <vibe@mistral.ai>), allowing multiple agents to coexist in the same container.
Human credentials are resolved in order:
1. Per-project git.human_name / git.human_email in project.yml
2. Global git.human_name / git.human_email in config.yml
3. git config --global user.name / user.email
4. Nobody <nobody@localhost>
Agent email addresses are GitHub-recognized and display with avatars in commit history.
Quick Reference¶
/workspace ← <state_dir>/tasks/<project>/<task>/workspace-dangerous:Z
/home/dev/.codex ← <mounts_dir>/_codex-config:z
/home/dev/.claude ← <mounts_dir>/_claude-config:z
/home/dev/.vibe ← <mounts_dir>/_vibe-config:z
/home/dev/.blablador ← <mounts_dir>/_blablador-config:z
/home/dev/.config/opencode ← <mounts_dir>/_opencode-config:z
/home/dev/.local/share/opencode ← <mounts_dir>/_opencode-data:z
/home/dev/.local/state ← <mounts_dir>/_opencode-state:z
Run terok config to see resolved paths on your system.