thor

mcp
Guvenlik Denetimi
Basarisiz
Health Uyari
  • No license — Repository has no license file
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 5 GitHub stars
Code Basarisiz
  • network request — Outbound network request in docker-compose.yml
  • process.env — Environment variable access in docker/ingress/10-thor-admin-emails.envsh.test.ts
  • rm -rf — Recursive force deletion command in docker/mitmproxy/entrypoint.sh
  • process.env — Environment variable access in docker/opencode/bin/rg.test.ts
Permissions Gecti
  • Permissions — No dangerous permissions requested

Bu listing icin henuz AI raporu yok.

SUMMARY

Ambient intelligence for product engineers.

README.md

Thor

An event-driven AI team member that watches Slack and scheduled jobs, resumes OpenCode sessions through the runner, and reaches external systems through remote-cli.

Architecture

ingress -> gateway -> runner -> opencode -> codex-lb -> ChatGPT
                           \
                            -> remote-cli -> MCP upstreams / CLI integrations
  • gateway accepts Slack, GitHub webhook, and cron events, batches them, and forwards them to the runner.
  • runner manages OpenCode session continuity and Slack progress updates.
  • remote-cli exposes POST /exec/* endpoints for git, gh, sandbox, scoutqa, metabase, aws, psql, MCP tool calls (Atlassian, Grafana, PostHog, Langfuse), direct Slack approval-card posting, and approval status/resolution.
  • codex-lb is an OpenAI-compatible proxy that fronts ChatGPT for opencode, pooling one or more ChatGPT account credentials so no paid OpenAI API key is needed. Its account/quota dashboard sits behind the same SSO + admin-email gate as /admin/.

Services

Service Port Package Role
codex-lb 2455 Docker image ChatGPT-backed OpenAI-compatible proxy
cron - docker/cron Scheduled prompts
mitmproxy 3080 docker/mitmproxy Explicit outbound HTTP(S) proxy
gateway 3002 @thor/gateway Slack/GitHub webhook ingestion and batching
remote-cli 3004 @thor/remote-cli CLI + MCP policy gateway
admin 3005 @thor/admin Admin dashboard and workspace configuration
ingress 8080 docker/ingress Reverse proxy + Vouch integration
opencode 4096 Docker image Headless agent runtime
runner 3000 @thor/runner Session lifecycle + Slack progress updates
vouch 9090 Docker image OAuth/SSO proxy

Quick Start

  1. Copy .env.example to .env and fill in the required secrets. Per-integration env vars are documented in each integration's doc (see Integrations below).
  2. Initialize the mitmproxy CA on the host:
./scripts/mitmproxy-ca-init.sh

All outbound HTTP(S) from OpenCode is routed through mitmproxy; see docs/feat/security-model.md Layer 1a for the routing path, built-in defaults, and custom rule format.

  1. Create /workspace/config/thor.json (on the host: docker-volumes/workspace/config/thor.json) from docs/examples/thor.json.

  2. Clone repos into the shared workspace:

docker compose run --rm remote-cli \
  git clone https://github.com/your-org/your-repo.git

If the stack is already running, use docker compose exec remote-cli ... instead.

  1. Start the stack:
mkdir -p docker-volumes/codex-lb && chmod 777 docker-volumes/codex-lb
docker compose up --build -d
curl http://localhost:8080/global/health

codex-lb runs as a non-root user and writes a SQLite store to /var/lib/codex-lb; pre-creating the host directory world-writable avoids a root-owned auto-mount.

  1. Link a ChatGPT account so opencode has an upstream model:

    Visit http://localhost:8080/dashboard (admin-gated by Vouch + THOR_ADMIN_EMAILS), sign in with Google, and add a ChatGPT account from the codex-lb dashboard. Once linked, opencode picks the model from its UI (the provider whitelist surfaces gpt-5.4, gpt-5.4-mini, gpt-5.5).

Integrations

Thor is an internal AI teammate for engineering and product work; it is not meant to mirror production infrastructure exactly. Each integration owns its own env vars, app/manifest setup, required permissions, and troubleshooting reasons.

  • Slackdocs/slack.md. Events API intake, signing-secret verification, per-channel repo override, app manifest.
  • GitHub Appdocs/github.md. Webhook intake, App permissions and event subscriptions, installation IDs, bot commit identity, CI wake gate.
  • Daytona sandboxesdocs/daytona.md. On-demand cloud sandboxes for project builds/tests/lints. Custom snapshot publishing.
  • Outbound HTTP(S) (mitmproxy)docs/feat/security-model.md Layer 1a. Routing path, built-in defaults (Atlassian/Slack/OpenAI), custom credential rules.

Runtime integration paths:

Integration Path Auth Notes
Git / GitHub CLI remote-cli /exec/git, /exec/gh GitHub App token Repo-scoped worktree edits
Atlassian MCP remote-cli /exec/mcp Auth header Read + approved writes
PostHog MCP remote-cli /exec/mcp API key Read + approved writes
Grafana MCP remote-cli /exec/mcp Service account token Logs and observability
Langfuse MCP remote-cli /exec/mcp API key pair Read-only LLM observability queries
Slack Web API gateway + remote-cli + OpenCode over mitmproxy Bot token Mentions, progress, approval cards, thread reads/writes
LaunchDarkly remote-cli /exec/ldcli Access token Read-only feature flag inspection
Metabase remote-cli /exec/metabase API key Read-only warehouse access
Postgres (psql) remote-cli /exec/psql Per-profile DB creds Read-only Postgres access by database alias

Common usage patterns:

  • PR merged, errors spike — a scheduled prompt checks telemetry, inspects recent merges through GitHub tools, prepares a fix in a worktree, and requests approval for the final write action.
  • Jira issue triage — a webhook or Slack prompt asks Thor to investigate an issue; Thor reads Jira, checks recent commits, and reports likely owners and suspects.
  • Daily delivery digest — a cron job asks Thor to summarize stale PRs, blocked issues, or failing tests and post the result to Slack.

Deployment Configuration

Integration-specific env vars live in each integration's doc. MCP integration credentials (Atlassian, PostHog, Grafana, Langfuse) support _<PROFILE_NAME> profile-suffixed overrides; multi-value integrations (Atlassian, Grafana, Langfuse) resolve all-or-nothing per scope — see docs/feat/profile.md. The psql passthrough's PSQL_DATABASES bundle is also profile-suffixed. Metabase uses the unsuffixed global METABASE_* values. Cross-cutting vars:

Variable Required Service Purpose
CRON_SECRET Yes gateway, cron Shared secret for cron endpoint auth
THOR_ADMIN_EMAILS Yes ingress Comma-separated authenticated Google emails allowed for OpenCode-backed and /admin/ ingress routes
THOR_INTERNAL_SECRET Yes remote-cli, gateway Secret-gates gateway↔remote-cli internal APIs
THOR_E2E_TEST_HELPERS No runner Enables secret-gated deterministic runner e2e helpers
RUNNER_BASE_URL Yes remote-cli Public base URL for Thor trigger viewer links in PR/Jira content
INGRESS_PORT No ingress Host port for the reverse proxy
ATLASSIAN_AUTH No remote-cli, mitmproxy Atlassian MCP auth header and mitmproxy default injection; required (with cloud ID) to enable Atlassian
ATLASSIAN_CLOUD_ID No remote-cli Atlassian cloud ID; required (with auth) to enable Atlassian; injected server-side into outbound MCP calls, hidden from model-facing tool schemas
POSTHOG_API_KEY No remote-cli PostHog MCP auth
GRAFANA_URL No remote-cli Grafana instance URL; required (with token + org ID) to enable Grafana
GRAFANA_SERVICE_ACCOUNT_TOKEN No remote-cli Grafana service account token; required (with URL + org ID) to enable Grafana
GRAFANA_ORG_ID No remote-cli Grafana org ID; required (with URL + token) to enable Grafana
LANGFUSE_BASE_URL No remote-cli Langfuse MCP base URL; all three LANGFUSE_* vars required to enable Langfuse
LANGFUSE_PUBLIC_KEY No remote-cli Langfuse MCP public key; required (with secret key + host) to enable Langfuse
LANGFUSE_SECRET_KEY No remote-cli Langfuse MCP secret key; required (with public key + host) to enable Langfuse
METABASE_URL No remote-cli Metabase instance URL
METABASE_API_KEY No remote-cli Metabase API key
METABASE_DATABASE_ID No remote-cli Metabase database ID
METABASE_ALLOWED_SCHEMAS No remote-cli Comma-separated schema allowlist
AWS_ACCESS_KEY_ID No remote-cli Credential for the aws CLI passthrough; omit to use an attached IAM role. Pair with AWS_SECRET_ACCESS_KEY (+ AWS_SESSION_TOKEN)
AWS_SECRET_ACCESS_KEY No remote-cli Secret for the aws CLI passthrough; required with AWS_ACCESS_KEY_ID
AWS_SESSION_TOKEN No remote-cli Session token for temporary aws CLI credentials
AWS_REGION / AWS_DEFAULT_REGION No remote-cli Default region for the aws CLI passthrough
PSQL_DATABASES[_<PROFILE>] No remote-cli JSON bundle of Postgres connection targets keyed by database alias for the read-only psql passthrough; profile-suffixed override falls back to the global value. See docs/feat/profile.md
VOUCH_GOOGLE_CLIENT_ID Yes vouch Google OAuth client ID
VOUCH_GOOGLE_CLIENT_SECRET Yes vouch Google OAuth client secret
VOUCH_JWT_SECRET Yes vouch Session JWT signing secret
VOUCH_ALLOWED_EMAIL_DOMAINS No compose -> vouch Rendered into Vouch's VOUCH_DOMAINS; comma-separated email domains, default scoutqa.cc
VOUCH_CALLBACK_URL No vouch OAuth callback URL
VOUCH_COOKIE_DOMAIN No vouch Cookie domain

Workspace config (thor.json)

Lives at /workspace/config/thor.json inside containers, docker-volumes/workspace/config/thor.json on the host. Hot-reloaded — no restart needed after edits. Use docs/examples/thor.json as a starting point and packages/common/src/proxies.ts as the reference for the built-in upstream catalog.

The file carries operator-maintained registries:

  • owners.<owner>.github_app_installation_id — GitHub App installation IDs. See docs/github.md §2.
  • profiles.<name>.channels[] / profiles.<name>.repos[] — integration credential routing profiles. channels[] also admits gated Slack surfaces; repos[] never admits Slack by itself. Profile shapes, repo fallback, conflict handling, and profile-suffixed environment variables are documented in docs/feat/profile.md. Slack admission details are in docs/slack.md §5.
  • mitmproxy[] / mitmproxy_passthrough[] — outbound credential rules and passthrough hosts. See docs/feat/security-model.md Layer 1a.
  • users[] — human attribution (see below).

Profile edits hot-reload — no service restart needed after thor.json changes. Profile selection is entirely harness-side; there is no agent-facing profile argument.

Human attribution (users[])

email must be the Jira account email; Thor may write the name/email into Co-authored-by: commit trailers and use the email to resolve Jira assignees.

{
  "users": [
    { "email": "[email protected]", "name": "Alice", "slack": "UABCDEF1", "github": "alice" },
    { "email": "[email protected]", "name": "Bob" }
  ]
}

To verify your entry, trigger Thor from Slack and look for attribution_applied with outcome: "applied" and your Slack id; skipped_no_user_record means the configured Slack id did not match the trigger.

The registry is maintained by operators from team Slack and GitHub membership records, with Jira account emails verified manually when needed. Keep source exports out of git if they contain personal data — commit only sanitized reconciliation decisions.

Operations Notes

  • Tell Thor about durable cross-task context through the memory tiers in docs/feat/memory.md: global, Slack channel, and person memory are runner-injected; repo context belongs in repo-local AGENTS.md, CLAUDE.md, and docs.
  • Clone source repos from the remote-cli container so git credentials and filesystem ownership stay consistent.
  • Repos under /workspace/repos are mounted read-only into OpenCode. Thor creates edits in /workspace/worktrees.
  • OpenCode and remote-cli share the same /tmp volume so temporary artifacts referenced by absolute path, such as slack-post-message --blocks-file /tmp/..., are readable by the posting service.
  • Scheduled prompts live in docker-volumes/workspace/cron/crontab.

Security Model

Thor contains untrusted input — agent, OpenCode wrappers, external webhooks — through layered controls. In short:

  • Vouch SSO + mitmproxy bound the network; remote-cli binds to 127.0.0.1 only.
  • Inbound webhooks are HMAC-verified (Slack signing secret, GitHub X-Hub-Signature-256); internal gateway↔remote-cli routes are gated with x-thor-internal-secret.
  • Channel/mention/self-loop gates filter authenticated traffic before it wakes the agent.
  • remote-cli is the only place tool policy is enforced: MCP allow/approve/hidden tiers, command allowlists, GitHub App installation tokens. OpenCode never holds direct upstream credentials.
  • Repos mount read-only into OpenCode; edits happen in /workspace/worktrees. Tool calls are audit-logged under /workspace/worklog.

See docs/feat/security-model.md for the full layered breakdown.

Testing

pnpm test
REMOTE_CLI_GIT_REPO_URL=https://github.com/owner/repo \
REMOTE_CLI_GITHUB_REPO=owner/repo \
  pnpm test:e2e
pnpm test:create-jira-approval-e2e # live Slack/OpenCode approval-card e2e for Atlassian approval-required tools
pnpm test:opencode-e2e # separate explicit OpenCode/LLM smoke path
pnpm typecheck

Yorumlar (0)

Sonuc bulunamadi