返回 Skill 列表
extension
分类: 开发与工程无需 API Key

kortix-system

完整的Kortix沙盒系统参考。涵盖:容器镜像、s6服务、文件系统布局、持久化模型、环境变量、密钥管理(用于设置/获取/删除环境变量的API)、端口、运行时、初始化脚本、云模式、桌面环境、部署(将应用部署到实时*.style.dev URL)、定时触发器(计划代理执行)、语义搜索(lss)、会话搜索与管理(API + 磁盘查询)、技能创建指南,以及所有已安装的工具。当您需要理解沙盒、调试服务、配置环境、设置API密钥/密钥、部署Web应用/API/静态站点、安排定时任务、进行语义文件搜索、查询会话数据或创建新技能时,请加载此技能。

person作者: jakexiaohubgithub

Kortix Sandbox System Architecture

The Kortix sandbox is a Docker container running Alpine Linux with a full XFCE desktop, noVNC remote access, and the OpenCode AI agent platform. This document is the definitive reference for how the system works.

Container Image

  • Base: lscr.io/linuxserver/webtop:latest (Alpine Linux + XFCE + noVNC)
  • Process manager: s6-overlay v3 with s6-rc.d (NOT the older services.d)
  • Entry point: /opt/startup.shexec unshare --pid --fork /init (PID namespace so s6 gets PID 1)
  • User: abc (UID 1000, set via PUID=1000). All services run as abc via s6-setuidgid abc.

Key Paths

  • /workspace/ — Docker volume. ONLY thing that persists across restarts. All user files live here.
  • /opt/opencode/ — OpenCode config: agents, tools, skills, plugins, commands, opencode.jsonc.
  • /opt/kortix-master/ — Kortix Master proxy + secret store + deployer server.
  • /app/secrets/ — Docker volume. Encrypted secret storage.

Persistence Model

Critical: Only TWO things persist across container restarts:

| Path | Volume | What | |---|---|---| | /workspace | workspace | All user data, agent memory, sessions, config | | /app/secrets | secrets_data | Encrypted API keys and environment variables |

Everything else is ephemeral. /opt, /usr, /etc, /tmp — all reset on container rebuild. If you install packages via apk add or npm install -g, they will be lost on rebuild. Only /workspace survives.


Projects — How They Work

A project in the Kortix sandbox is a git repository. The project system is entirely automatic — no manual creation required.

Detection

When OpenCode encounters a directory, it runs Project.fromDirectory(directory):

  1. Walk up the directory tree looking for .git
  2. If found, extract the first root commit SHA (git rev-list --max-parents=0 --all)
  3. That SHA becomes the project's permanent, stable ID
  4. Cache the ID in .git/opencode for fast subsequent lookups
  5. If no .git found or no commits exist, fall back to id: "global"

Discovery (Workspace Scanning)

On startup and periodically, OpenCode scans $KORTIX_WORKSPACE (/workspace) for all .git directories. Every git repo found becomes a project automatically:

  • Clone a repo into /workspace/my-app/ → it becomes a project
  • Create a new repo with git init && git commit → it becomes a project
  • Delete the repo → it disappears from the project list

Identity

Project IDs are based on git history, not filesystem paths:

  • Renaming or moving a repo keeps the same project ID
  • Cloning the same repo on another machine produces the same ID
  • IDs are 40-character hex strings (SHA-1 commit hashes)

Project-Session Relationship

Sessions are scoped to projects:

  • Every session has a projectID field linking it to a project
  • Listing sessions only returns those belonging to the current project
  • Creating a session automatically assigns it to the current project

Project API

| Method | Route | Description | |---|---|---| | GET | /project | List all projects (triggers workspace scan) | | GET | /project/current | Get the current project for this request's directory | | PATCH | /project/:projectID | Update project name, icon, or commands |


Services & Ports

All services are managed by s6-rc.d as longruns. Service scripts live at /etc/s6-overlay/s6-rc.d/svc-*/run.

| Service | Script | Internal Port | Host Port | Description | |---|---|---|---|---| | Kortix Master | svc-kortix-master | 8000 | 14000 | Reverse proxy + secret store + deployer. Entry point for API access. | | OpenCode Web | svc-opencode-web | 3111 | 14001 | Web UI (SolidJS app from opencode-ai npm package) | | OpenCode Serve | svc-opencode-serve | 4096 | (proxied via 8000) | Backend API server. Not exposed directly — proxied by Kortix Master. | | Desktop (noVNC) | (base image) | 6080 / 6081 | 14002 / 14003 | XFCE desktop via VNC. HTTP / HTTPS. | | Presentation Viewer | svc-presentation-viewer | 3210 | 14004 | Serves generated slide decks | | Agent Browser Stream | (agent-browser) | 9223 | 14005 | Playwright browser automation WebSocket | | Agent Browser Viewer | svc-agent-browser-viewer | 9224 | 14006 | Browser session viewer UI (HTML + SSE bridge) | | lss-sync | svc-lss-sync | — | — | File watcher daemon for semantic search indexing |

Kortix Master Details

Kortix Master (/opt/kortix-master/src/index.ts, runs via Bun on port 8000) is the main entry point. It handles:

| Route | Target | Description | |---|---|---| | /env/* | Local (SecretStore) | Secret/env var management (GET/POST/PUT/DELETE) | | /api/integrations/* | Kortix API (proxied) | OAuth integration tools (7 routes including internal /token) | | /kortix/deploy/* | Local (Deployer) | Local app deployment (start/stop/status/logs) | | /kortix/health | Local | Health check (includes version, OpenCode readiness) | | /kortix/ports | Local | Container→host port mappings | | /kortix/update | Local | Self-update mechanism (POST only) | | /lss/search | Local (lss CLI) | Semantic search API | | /lss/status | Local (lss CLI) | LSS index health | | /proxy/:port/* | localhost:{port} | Dynamic port proxy (any service inside container) | | /* (catch-all) | localhost:4096 | Everything else proxied to OpenCode API |

The dynamic port proxy (/proxy/:port/*) injects a Service Worker into HTML responses to rewrite all subsequent requests through the proxy prefix. It also handles WebSocket upgrades for proxied services.

Kortix Master Authentication

The master uses a localhost-bypass auth model:

  • From inside the sandbox (localhost/loopback): No auth required. Curl, tools, scripts running inside the container can call localhost:8000 freely — no tokens, no headers.
  • From outside the sandbox (kortix-api, frontend proxy, host machine): Must provide INTERNAL_SERVICE_KEY as a Bearer token or ?token= query param.

Two tokens exist with opposite directions:

| Token | Direction | Purpose | |---|---|---| | INTERNAL_SERVICE_KEY | external → sandbox | How kortix-api authenticates TO the sandbox. Required for external requests to port 8000 (mapped to host port 14000). | | KORTIX_TOKEN | sandbox → external | How the sandbox authenticates TO kortix-api. Used for outbound requests (cron, integrations, LLM proxy, deployments). Also used as the SecretStore encryption key. |

Unauthenticated routes (always open, even externally): /kortix/health, /docs, /docs/openapi.json.

External access example (from host machine or another container):

curl http://127.0.0.1:14000/env \
  -H "Authorization: Bearer $INTERNAL_SERVICE_KEY"

Environment Variables

Core Config

| Variable | Value | Description | |---|---|---| | OPENCODE_CONFIG_DIR | /opt/opencode | Where agents, tools, skills, plugins live | | KORTIX_WORKSPACE | /workspace | Workspace root | | OPENCODE_FILE_ROOT | / | File explorer shows full filesystem (set in svc-opencode-serve) | | OPENCODE_PERMISSION | {"*":"allow"} | Auto-approve all tool calls (set in docker-compose, not Dockerfile) | | BUN_PTY_LIB | /opt/bun-pty-musl/librust_pty.so | Musl-compatible PTY library path | | BUN_INSTALL | /opt/bun | Bun installation directory | | LSS_DIR | /workspace/.lss | Semantic search index location | | HOME | /workspace | Set by service scripts (not globally) | | DISPLAY | :1 | X11 display for desktop apps |

Agent Browser Config

| Variable | Value | |---|---| | AGENT_BROWSER_EXECUTABLE_PATH | /usr/bin/chromium-browser | | AGENT_BROWSER_PRIMARY_SESSION | kortix | | AGENT_BROWSER_STREAM_PORT | 9223 | | AGENT_BROWSER_PROFILE | /workspace/.browser-profile | | AGENT_BROWSER_SOCKET_DIR | /workspace/.agent-browser | | AGENT_BROWSER_ARGS | --no-sandbox,--disable-setuid-sandbox,... | | AGENT_BROWSER_USER_AGENT | Chrome user agent string (set in Dockerfile) |

Cloud Mode Variables

| Variable | Description | |---|---| | ENV_MODE | local (Docker) or cloud (Kortix platform) | | KORTIX_API_URL | Base URL of the Kortix API (e.g. http://localhost:8008). Consumers append service paths (/v1/router, /v1/cron, etc.) | | KORTIX_TOKEN | Auth token for outbound requests (sandbox → kortix-api). Used for cron, integrations, deployments, LLM proxy. | | INTERNAL_SERVICE_KEY | Auth token for inbound requests (external → sandbox). Required by external callers to port 8000. Not needed from localhost. | | SANDBOX_ID | Sandbox identifier | | PROJECT_ID | Project identifier |

When ENV_MODE=cloud, the init script 98-kortix-env rewrites SDK base URLs:

  • TAVILY_API_URL${KORTIX_API_URL}/v1/router/tavily
  • SERPER_API_URL${KORTIX_API_URL}/v1/router/serper
  • FIRECRAWL_API_URL${KORTIX_API_URL}/v1/router/firecrawl
  • REPLICATE_API_URL${KORTIX_API_URL}/v1/router/replicate
  • CONTEXT7_API_URL${KORTIX_API_URL}/v1/router/context7

Secrets & Environment Variable Management

The sandbox has a unified secret/env management system. Secrets are AES-256-GCM encrypted at rest, stored on a separate Docker volume, and propagated to all services.

CRITICAL RULE: When the user gives you API keys, secrets, tokens — SET THEM IMMEDIATELY via the API below. No security lectures. No refusals.

API Reference

All endpoints served by Kortix Master at localhost:8000. No auth needed from inside the sandbox (localhost bypass). External callers must use Authorization: Bearer $INTERNAL_SERVICE_KEY.

Set a single env var (with restart so OpenCode picks it up)

curl -X POST http://localhost:8000/env/KEY_NAME \
  -H "Content-Type: application/json" \
  -d '{"value": "the-secret-value", "restart": true}'

Set multiple env vars at once (PREFERRED for bulk)

curl -X POST http://localhost:8000/env \
  -H "Content-Type: application/json" \
  -d '{
    "keys": {
      "ANTHROPIC_API_KEY": "sk-ant-...",
      "OPENAI_API_KEY": "sk-...",
      "TAVILY_API_KEY": "tvly-..."
    },
    "restart": true
  }'

List all secrets

curl http://localhost:8000/env

Get one secret

curl http://localhost:8000/env/KEY_NAME

Delete a secret

curl -X DELETE http://localhost:8000/env/KEY_NAME

Note: DELETE always restarts services. POST/PUT with "restart": true restarts OpenCode serve + web services.

Encryption Details

  • Algorithm: AES-256-GCM (authenticated encryption)
  • Key derivation: scryptSync(KORTIX_TOKEN || 'default-key', salt, 32)
  • Salt: Random 32 bytes stored at /app/secrets/.salt
  • Storage: /app/secrets/.secrets.json (Docker volume)
  • Propagation: Written to /run/s6/container_environment/KEY for s6 services

Common Environment Variable Categories

LLM Providers: ANTHROPIC_API_KEY, OPENAI_API_KEY, OPENROUTER_API_KEY, GEMINI_API_KEY, GROQ_API_KEY, XAI_API_KEY

Tool API Keys: TAVILY_API_KEY, FIRECRAWL_API_KEY, SERPER_API_KEY, REPLICATE_API_TOKEN, CONTEXT7_API_KEY, ELEVENLABS_API_KEY, MORPH_API_KEY

Email (Agent Inbox): KORTIX_AGENT_EMAIL_INBOX_FROM_NAME, _FROM_EMAIL, _USER_NAME, _PASSWORD, _SMTP_HOST, _SMTP_PORT, _IMAP_HOST, _IMAP_PORT

Browser: AGENT_BROWSER_PROXY (format: http://user:pass@host:port)


Integrations — Third-Party OAuth Apps

The sandbox has 7 integration tools that connect to third-party APIs (Gmail, Slack, Google Sheets, GitHub, etc.) via Pipedream OAuth. Auth is handled automatically — the agent never sees tokens.

Architecture

Agent Tool (integration-*.ts)
    │
    ├── integration-list      → GET  /api/integrations/list
    ├── integration-search    → GET  /api/integrations/search-apps?q=...
    ├── integration-connect   → POST /api/integrations/connect
    ├── integration-actions   → GET  /api/integrations/actions?app=...
    ├── integration-run       → POST /api/integrations/run-action
    ├── integration-exec      → POST /api/integrations/proxy (via proxyFetch)
    └── integration-request   → POST /api/integrations/proxy
            │
            ▼
    Kortix Master (/api/integrations/*)
            │
            ▼
    Kortix API (POST /v1/integrations/*)
            │
            ▼
    Pipedream (OAuth token management + action execution)

All tools communicate with Kortix Master at localhost:8000/api/integrations/* (no auth needed — localhost bypass), which then proxies outbound to the Kortix API with KORTIX_TOKEN auth.

Tool Reference

| Tool | Purpose | When to Use | |---|---|---| | integration-list | List connected apps | Check what's available before using other tools | | integration-search | Search available apps by keyword | Find the correct app slug (e.g., q='gmail'gmail) | | integration-connect | Generate OAuth connect URL | Returns a dashboard URL the user clicks to authorize | | integration-actions | Discover actions for an app | Find action keys + required params (e.g., gmail-send-email) | | integration-run | Execute a Pipedream action | Run structured actions without knowing API details | | integration-exec | Execute custom Node.js code | For custom API calls — use proxyFetch(url, init) instead of fetch() | | integration-request | Make raw authenticated HTTP request | Direct API calls with auto-injected OAuth credentials |

Workflow

  1. Check what's connected: integration-list
  2. Search for an app if needed: integration-search({ q: "gmail" })
  3. Connect if not linked: integration-connect({ app: "gmail" }) → user clicks URL
  4. Discover actions: integration-actions({ app: "gmail", q: "send" })
  5. Execute: integration-run({ app: "gmail", action_key: "gmail-send-email", props: { to, subject, body } })

For Custom API Calls (integration-exec)

Use proxyFetch(url, init) — it works like fetch() but OAuth credentials are injected automatically by the proxy. Never set Authorization headers manually.

// Example: List Gmail labels
const res = await proxyFetch('https://gmail.googleapis.com/gmail/v1/users/me/labels');
const data = await res.json();
console.log(data);

Deployments

The sandbox has two deployment systems depending on the mode:

Cloud Deployments — Kortix Deployments API (*.style.dev)

In cloud mode, deploy to live URLs via the Kortix Deployments API. The API handles everything server-side — no SDK, no user-facing API keys.

Capabilities:

  • 4 source types: Git repo, inline code, local files, tar URL
  • Auto-detects Next.js, Vite, Expo — TypeScript works out of the box
  • Free *.style.dev subdomains with instant SSL
  • Node.js only — no Python, Ruby, Go
  • Port 3000 — all servers must listen on port 3000

API Call Pattern

curl -X POST "$KORTIX_API_URL/v1/deployments" \
  -H "Authorization: Bearer $KORTIX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ ... }'

Deploy Examples

Git repo:

{
  "source_type": "git",
  "source_ref": "https://github.com/user/repo",
  "domains": ["my-app-x7k2.style.dev"],
  "build": true
}

Inline code (Express API):

{
  "source_type": "code",
  "code": "import express from 'express';\nconst app = express();\napp.get('/', (req, res) => res.json({ status: 'ok' }));\napp.listen(3000);",
  "node_modules": { "express": "^4.18.2" },
  "domains": ["api-e5f6.style.dev"]
}

Local files (pre-built):

import { readFileSync, readdirSync, statSync } from 'fs';
import { join, relative } from 'path';

function readFilesRecursive(dir: string, base?: string): Array<{path: string, content: string, encoding: string}> {
  const result: Array<{path: string, content: string, encoding: string}> = [];
  base = base ?? dir;
  for (const entry of readdirSync(dir)) {
    if (entry === 'node_modules') continue;
    const full = join(dir, entry);
    if (statSync(full).isDirectory()) result.push(...readFilesRecursive(full, base));
    else result.push({ path: relative(base, full), content: readFileSync(full).toString('base64'), encoding: 'base64' });
  }
  return result;
}
// POST body: { source_type: 'files', files: readFilesRecursive('./dist'), entrypoint: 'server.js', domains: ['my-site.style.dev'] }

Cloud Deployments API Request Schema

{
  source_type: 'git' | 'code' | 'files' | 'tar',
  source_ref?: string,       // Git repo URL (git)
  branch?: string,           // Git branch (git)
  root_path?: string,        // Monorepo sub-path (git)
  code?: string,             // Inline JS/TS (code)
  files?: Array<{ path: string, content: string, encoding?: string }>,  // (files)
  tar_url?: string,          // Archive URL (tar)
  domains: string[],         // Required. Use "slug.style.dev"
  build?: boolean | { command?: string, outDir?: string, envVars?: Record<string, string> },
  env_vars?: Record<string, string>,     // Runtime env vars
  node_modules?: Record<string, string>, // Only for 'code' deploys
  entrypoint?: string,
  static_only?: boolean,
  public_dir?: string,
  clean_urls?: boolean,
  framework?: string,        // Hint: 'nextjs', 'vite', 'static'
}

Other Cloud API Endpoints

# List / Get / Logs / Stop / Redeploy / Delete
curl "$KORTIX_API_URL/v1/deployments" -H "Authorization: Bearer $KORTIX_TOKEN"
curl "$KORTIX_API_URL/v1/deployments/{id}" -H "Authorization: Bearer $KORTIX_TOKEN"
curl "$KORTIX_API_URL/v1/deployments/{id}/logs" -H "Authorization: Bearer $KORTIX_TOKEN"
curl -X POST "$KORTIX_API_URL/v1/deployments/{id}/stop" -H "Authorization: Bearer $KORTIX_TOKEN"
curl -X POST "$KORTIX_API_URL/v1/deployments/{id}/redeploy" -H "Authorization: Bearer $KORTIX_TOKEN"
curl -X DELETE "$KORTIX_API_URL/v1/deployments/{id}" -H "Authorization: Bearer $KORTIX_TOKEN"

Cloud Deploy Hard-Won Lessons

  1. Runtime is Node.js: Deno.serve() and app.fire() do NOT work.
  2. Port 3000: All servers must listen on port 3000.
  3. Static sites need a server OR static_only: true.
  4. env_vars are runtime-only: Use build.envVars for build-time variables.
  5. Include your lockfile: Never include node_modules.
  6. Cold starts: First request may take 10-15 seconds.
  7. Next.js requires output: "standalone" and images: { unoptimized: true }.

Local Deployments — Kortix Master Deployer

In local mode (or for preview), the Kortix Master has a built-in deployer at /kortix/deploy. It runs apps as local processes on random ports (10000-60000) inside the container, accessible via the dynamic port proxy.

Capabilities:

  • Auto-detects Next.js, Vite, CRA, Node.js, Python, static HTML
  • Runs locally — no external infrastructure needed
  • Random ports — each deployment gets an available port
  • Accessible via http://localhost:8000/proxy/{port}/

Local Deploy API (no auth needed from inside sandbox)

MASTER_URL="http://localhost:8000"

# Deploy an app (auto-detects framework)
curl -X POST "$MASTER_URL/kortix/deploy" \
  -H "Content-Type: application/json" \
  -d '{
    "deploymentId": "my-app",
    "sourceType": "files",
    "sourcePath": "/workspace/my-app"
  }'
# Returns: { success, port, pid, framework, logs }

# With git source
curl -X POST "$MASTER_URL/kortix/deploy" \
  -H "Content-Type: application/json" \
  -d '{
    "deploymentId": "my-app",
    "sourceType": "git",
    "sourceRef": "https://github.com/user/repo",
    "sourcePath": "/workspace/my-app"
  }'

# List running deployments
curl "$MASTER_URL/kortix/deploy"

# Get status / logs
curl "$MASTER_URL/kortix/deploy/my-app/status"
curl "$MASTER_URL/kortix/deploy/my-app/logs"

# Stop
curl -X POST "$MASTER_URL/kortix/deploy/my-app/stop"

Local Deploy Config

{
  deploymentId: string,      // Required — unique identifier
  sourceType: 'git' | 'code' | 'files' | 'tar',
  sourceRef?: string,        // Git URL (for sourceType: 'git')
  sourcePath: string,        // Path on filesystem (default: /workspace)
  framework?: string,        // Auto-detected if not provided
  envVarKeys?: string[],     // Env var names to pass to the app
  buildConfig?: Record<string, unknown>,
  entrypoint?: string,       // Custom start command
}

Framework Detection (Local)

| Detected | Install | Build | Start | Default Port | |---|---|---|---|---| | nextjs | npm install | npm run build | npm start | 3000 | | vite | npm install | npm run build | npx vite preview --host 0.0.0.0 --port {PORT} | 4173 | | cra | npm install | npm run build | npx serve -s build -l {PORT} | 3000 | | node | npm install | — | npm start | 3000 | | python | pip install -r requirements.txt | — | python app.py | 8080 | | static | — | — | npx serve -s . -l {PORT} | 3000 |

After deploy, the app is accessible at http://localhost:8000/proxy/{port}/ which Kortix Master proxies to the local port.


Cron Triggers — Scheduled Agent Execution

The Kortix Cron service manages scheduled triggers that fire agents on cron schedules using pg_cron.

Quick Start (Local Mode — No Auth Needed)

SANDBOX_ID="${SANDBOX_ID:-kortix-sandbox}"

curl -X POST "$KORTIX_API_URL/v1/cron/triggers" \
  -H "Content-Type: application/json" \
  -d "{
    \"sandbox_id\": \"$SANDBOX_ID\",
    \"name\": \"Daily Report\",
    \"cron_expr\": \"0 0 9 * * *\",
    \"prompt\": \"Generate the daily status report\"
  }"

Cron Expression Format (6-field with seconds)

second minute hour day month weekday
0      */5    *    *   *     *        ← Every 5 minutes
0      0      9    *   *     *        ← Daily at 9 AM
0      0      8    *   *     1        ← Every Monday at 8 AM

Note: pg_cron strips seconds internally. Minimum resolution is 1 minute.

API Reference

# Create trigger
curl -X POST "$KORTIX_API_URL/v1/cron/triggers" -H "Content-Type: application/json" \
  -d '{"sandbox_id":"...","name":"...","cron_expr":"...","prompt":"..."}'

# List triggers
curl "$KORTIX_API_URL/v1/cron/triggers?sandbox_id=$SANDBOX_ID"

# Get/Update/Delete trigger
curl "$KORTIX_API_URL/v1/cron/triggers/{id}"
curl -X PATCH "$KORTIX_API_URL/v1/cron/triggers/{id}" -d '{"prompt":"new prompt"}'
curl -X DELETE "$KORTIX_API_URL/v1/cron/triggers/{id}"

# Pause/Resume/Fire now
curl -X POST "$KORTIX_API_URL/v1/cron/triggers/{id}/pause"
curl -X POST "$KORTIX_API_URL/v1/cron/triggers/{id}/resume"
curl -X POST "$KORTIX_API_URL/v1/cron/triggers/{id}/run"

# Execution history
curl "$KORTIX_API_URL/v1/cron/executions?limit=20"
curl "$KORTIX_API_URL/v1/cron/executions/by-trigger/{triggerId}"

Trigger Properties

| Field | Required | Description | |---|---|---| | sandbox_id | Yes | Target sandbox UUID | | name | Yes | Human-readable name | | cron_expr | Yes | 6-field cron expression | | prompt | Yes | Prompt sent to agent | | timezone | No | IANA timezone (default: UTC) | | agent_name | No | Target agent (e.g., kortix) | | model_id | No | Model (kortix/basic = Sonnet, kortix/power = Opus) | | session_mode | No | new (default) or reuse |


Semantic Search (lss)

Full semantic search engine powered by lss. Combines BM25 full-text + embedding similarity. Background daemon (lss-sync) auto-indexes file changes in real time.

Quick Reference

# Search everything
lss "your query" -p /workspace -k 10 --json

# Filter by file type
lss "auth logic" -p /workspace -e .py -e .ts -k 10 --json

# Exclude file types
lss "config" -p /workspace -E .json -E .yaml -k 10 --json

# Force re-index
lss index /workspace/important-file.md

# Index stats
lss status

HTTP API (via Kortix Master — no auth needed from inside sandbox)

# Semantic search via HTTP
curl "http://localhost:8000/lss/search?q=auth+logic&k=10&path=/workspace&ext=.ts,.py"

# Index health
curl "http://localhost:8000/lss/status"

Search Filters

| Flag | Description | Example | |---|---|---| | -e EXT | Include only extensions | -e .py -e .ts | | -E EXT | Exclude extensions | -E .json -E .yaml | | -x REGEX | Exclude chunks matching regex | -x 'test_' -x 'TODO' | | -k N | Number of results | -k 10 | | --no-index | Skip re-indexing (faster) | |

JSON Output

lss "query" -p /workspace --json -k 10
# Returns: [{ "query": "...", "hits": [{ "file_path": "...", "score": 0.03, "snippet": "..." }] }]

When to Use lss vs grep

| Use lss | Use grep | |---|---| | Conceptual queries | Exact strings | | Fuzzy matching | Variable names | | Cross-file discovery | Known patterns |


Memory & Context Management

For the full memory, context, and filesystem persistence guide, load the memory-context-management skill.

Key rules:

  • The filesystem is forever persistent. /workspace survives restarts, rebuilds, reboots. Write plans and notes to disk for anything that should survive across sessions.
  • kortix-sys-oc-plugin auto-captures observations, consolidates into LTM during compaction, and injects your session ID + relevant LTM on every turn.
  • Four tools: mem_search, mem_save, session_list, session_get — all in one plugin.
  • Both systems reinforce each other: files on disk are ground truth; the memory plugin surfaces relevant knowledge automatically.

Session Search & Management

For the full session search guide (plugin tools, SQL, grep, lss, REST API, workflows), load the session-search skill.

Quick reference below; the session-search skill has the complete decision tree and all query examples.

Key Facts

  • Primary storage: SQLite at /workspace/.local/share/opencode/opencode.db
  • Legacy JSON: /workspace/.local/share/opencode/storage/ (session, message, part, todo)
  • Plugin tools: session_list (browse/filter) + session_get (retrieve with TTC compression)
  • REST API: GET /session, GET /session/:id/message, GET /session/status, DELETE /session/:id (via localhost:8000 or :4096)
  • Direct SQL: sqlite3 /workspace/.local/share/opencode/opencode.db
  • Grep: grep -rl 'keyword' /workspace/.local/share/opencode/storage/part/
  • Semantic: lss "query" -p /workspace/.local/share/opencode/storage/ -k 10 --json

Commands

Slash commands trigger structured workflows. Defined at /opt/opencode/commands/ as markdown files with frontmatter.

| Command | File | Purpose | |---|---|---| | /onboarding | onboarding.md | First-run gatekeeper — researches the user, builds a profile, demos capabilities, unlocks dashboard |

The onboarding command runs automatically on first use. It searches the web for the user, builds a profile, and fires POST /env/ONBOARDING_COMPLETE to unlock the dashboard.


Skill Creation Guide

When creating new skills to extend agent capabilities:

Structure

skill-name/
├── SKILL.md          # Required: YAML frontmatter + markdown instructions
├── scripts/          # Optional: executable code
├── references/       # Optional: supplementary docs
└── assets/           # Optional: templates, files

SKILL.md Format

---
name: my-skill
description: "Comprehensive description with trigger phrases. The agent reads ONLY this to decide when to load the skill."
---

# Instructions loaded when skill triggers

Principles

  1. Concise is key — The context window is shared. Only include what the model doesn't already know.
  2. Description is the trigger — Only name and description are always in context. Make the description comprehensive.
  3. Progressive disclosure — Keep SKILL.md under 500 lines. Use references/ for large docs.
  4. Prefer examples over explanations — Show, don't tell.
  5. Set appropriate freedom — High freedom for flexible tasks, low freedom for fragile operations.

Init Scripts (Boot Order)

| Script | File | What It Does | |---|---|---| | 96 | 96-fix-bun-pty | Patches bun-pty .so files for musl compatibility | | 97 | 97-secrets-to-s6-env | Syncs encrypted secrets → s6 environment. Seeds template keys on first run. | | 98 | 98-kortix-env | Cloud mode: rewrites SDK base URLs to route through Kortix API proxy | | 99 | 99-customize | XFCE dark theme, wallpaper, terminal config |

Runtimes & Tools

| Runtime | Location | Notes | |---|---|---| | Node.js + npm | /usr/bin/node | System install | | Bun | /opt/bun/bin/bun | Also at /usr/local/bin/bun | | Python 3 + pip | /usr/bin/python3 | With virtualenv, uv, numpy, playwright | | Bash | /bin/bash | Alpine default |

| Tool | Description | |---|---| | opencode | OpenCode CLI — opencode serve (API), opencode web (UI) | | agent-browser | Headless browser automation (npm global, uses system Chromium) | | lss-sync | File watcher for semantic search indexing | | lss | Semantic search CLI (BM25 + embeddings) | | git, curl, uv, bun | Standard tooling |

OpenCode Configuration

Main config at /opt/opencode/opencode.jsonc:

  • Default agent: kortix
  • Built-in agents: build, plan, explore, general are available but not default (disable lines are commented out in config)
  • Permission: allow (all tool calls auto-approved)
  • Plugins: opencode-pty, ./plugin/worktree.ts, kortix-sys-oc-plugin (memory + session tools)
  • MCP servers: Context7 (remote, for documentation lookup)
  • Provider: Kortix router (OpenAI-compatible) with two models: kortix/basic and kortix/power
  • Auto-update: enabled (autoupdate: true)

Agents

Located at /opt/opencode/agents/:

| Agent | File | Mode | Role | |---|---|---|---| | kortix | kortix.md | primary | The agent. Plans, explores, builds. Self-spawns for parallel work. Loads skills for domain knowledge. |

No specialist subagents. Domain knowledge lives in skills loaded on demand via skill().

Custom Tools

Located at /opt/opencode/tools/:

| Tool | File | Description | |---|---|---| | Web Search | web-search.ts | Tavily search API | | Image Search | image-search.ts | Serper Google Images API | | Image Gen | image-gen.ts | Replicate image generation (Flux) | | Video Gen | video-gen.ts | Replicate video generation (Seedance) | | Scrape Webpage | scrape-webpage.ts | Firecrawl web scraping | | Presentation Gen | presentation-gen.ts | HTML slide deck creation | | Show | show.ts | Present outputs to user UI (images, files, URLs, text, errors) | | Cron Triggers | cron-triggers.ts | Scheduled agent execution |

| Integration List | integration-list.ts | List connected OAuth apps | | Integration Search | integration-search.ts | Search available apps by keyword | | Integration Connect | integration-connect.ts | Generate OAuth connect URL for user | | Integration Actions | integration-actions.ts | Discover actions for a connected app | | Integration Run | integration-run.ts | Execute a Pipedream action (structured) | | Integration Exec | integration-exec.ts | Execute Node.js code with proxyFetch() | | Integration Request | integration-request.ts | Raw authenticated HTTP request |

Debugging

Check service status

ps aux | grep -E "(opencode|kortix-master|lss-sync|bun)"
ls /run/service/

Restart a service

kill $(pgrep -f "opencode serve")  # s6 auto-restarts longruns

Check health

curl http://localhost:8000/kortix/health
curl http://localhost:8000/lss/status

Common Issues

| Problem | Fix | |---|---| | opencode not found | PATH="/opt/bun/bin:/usr/local/bin:/usr/bin:/bin" | | bun-pty segfault | Check BUN_PTY_LIB, run 96-fix-bun-pty | | Secrets not in env | Set via curl localhost:8000/env with restart: true | | Cloud SDK calls fail | Check KORTIX_API_URL is set | | Integration tools fail | Check KORTIX_TOKEN is set and integrations are connected | | Local deploy fails | Check curl localhost:8000/kortix/deploy for running deploys |

Docker Compose

docker compose -f sandbox/docker-compose.yml up --build -d
docker compose -f sandbox/docker-compose.yml logs -f
docker exec -it kortix-sandbox bash
docker exec -it -u abc kortix-sandbox bash

Volumes

| Volume | Mount | Purpose | |---|---|---| | workspace | /workspace | All persistent data | | secrets_data | /app/secrets | Encrypted secrets |

Resource Limits

  • shm_size: 2gb — Required for Chromium
  • cap_add: SYS_ADMIN — Required for PID namespace
  • security_opt: seccomp=unconfined — Required for Chromium sandbox