Quickstart — Build a portfolio site with Claude Code
This quickstart takes about 15 minutes. You will:
- Install the SDK and bring up an admin server.
- Create a Sealed Identity holding your Anthropic and GitHub credentials.
- Define a workflow that dispatches Claude Code inside an isolated Docker sandbox to build a portfolio site.
- Trigger the workflow and watch the artifact commit to GitHub.
If you want a hello-world agent with no sandbox or per-customer credentials, start with Minimal Setup instead. This page exercises the full stack.
What happens, in one sentence: Sagewai's worker dispatches a CLI agent (Claude Code) inside an identity-isolated Docker sandbox; your customer's credentials are injected at sandbox-start and never touch the worker host. See Architecture: Execution modes — Mode 3 for the full topology.
Prerequisites
- Python 3.10 or later (
uvrecommended for venv management) - Docker installed and running (the default sandbox backend)
- An Anthropic API key (Claude Code uses this — Tier-2 in Sagewai terminology)
- A GitHub personal access token with
reposcope (artifact upload uses this — also Tier-2) - A target GitHub repository to receive the generated site (an empty repo is fine)
- An operator-side LLM key for orchestration (the "Tier-1" key —
see Security tiers). For this
quickstart, set
ORCHESTRATION_OPENAI_KEYor use a local Ollama URL.
1. Install Sagewai
uv add 'sagewai[fastapi,postgres]'
# OR with pip:
pip install 'sagewai[fastapi,postgres]'
The fastapi extra brings the admin server. The postgres extra
brings durable workflow storage; SQLite works for local exploration
but production should use Postgres.
2. Bring up the admin server + a worker
In one terminal, start the admin server:
sagewai admin serve --host 0.0.0.0 --port 8000
Open http://localhost:8000 in a browser and complete the first-time setup wizard (org name, admin email, admin password — see the Admin Panel guide). This creates the operator account.
In a second terminal, start a worker with the Docker sandbox backend:
export ORCHESTRATION_OPENAI_KEY=sk-... # Tier-1 — orchestration brain
sagewai worker start \
--pool default \
--sandbox-mode per_run \
--sandbox-backend docker \
--sandbox-image ghcr.io/sagewai/sandbox-claude-code:latest \
--sandbox-network egress-only
The worker registers with the admin server, advertises capability
labels (sandbox.backend=docker, sandbox.image_variants=claude-code),
and is ready to claim runs.
3. Create a Sealed Identity for your customer credentials
A Sealed Identity is a named bundle of Tier-2 credentials and
behavior knobs. Claude Code reads its API key from os.environ
inside the sandbox; the env values come from the Identity at
sandbox-start time.
In the admin panel, go to Sealed → Profiles → Create. Name the
profile portfolio-customer-X and add:
ANTHROPIC_API_KEY=sk-ant-... ← Claude Code uses this
GITHUB_TOKEN=ghp_... ← git push uses this
The profile is stored encrypted at rest (Fernet) in
~/.sagewai/profiles.json. The admin server holds the master key;
the worker host and control plane never see the plaintext values
again. See Security tiers for
the trust boundary.
4. Define the workflow
Note on the code below. The decorator API for per-step modes is still stabilising — the canonical reference is the Execution modes architecture page. The example here matches the shape that page commits to. If a parameter name has evolved by the time you read this, the Workflows reference is the authoritative API.
Create build_portfolio.py:
import asyncio
from sagewai import DurableWorkflow, UniversalAgent
from sagewai.core.stores.postgres import PostgresStore
from sagewai.sandbox import SandboxMode
store = PostgresStore(database_url="postgresql://localhost/sagewai")
planner = UniversalAgent(
name="planner",
model="gpt-4o-mini", # Tier-1 LLM — operator pays
)
wf = DurableWorkflow(name="portfolio-builder", store=store)
@wf.step("plan")
async def plan(brief: str) -> dict:
"""Mode 0 — pure orchestration on the worker."""
result = await planner.chat(
f"Extract structured requirements from this portfolio brief: {brief}"
)
return {"brief": brief, "requirements": result}
@wf.step(
"build_site",
sandbox_mode=SandboxMode.PER_RUN,
sandbox_image="ghcr.io/sagewai/sandbox-claude-code:latest",
security_profile_ref="portfolio-customer-X",
cli_agent="claude-code",
artifact_destination={
"kind": "github",
"repo": "your-org/portfolio-site",
"branch": "main",
},
)
async def build_site(plan: dict) -> str:
"""Mode 3 — Claude Code in a sandbox, push to GitHub."""
return await wf.dispatch_cli_agent(
prompt=f"Scaffold a Next.js portfolio site per this brief: {plan['requirements']}",
workdir="/workspace",
)
@wf.step("summarise")
async def summarise(artifact_url: str) -> str:
"""Mode 0 — orchestration."""
result = await planner.chat(
f"Write a one-paragraph completion message for site at {artifact_url}"
)
return result
async def main():
await store.initialize()
run_id = await wf.enqueue(input_data={"brief": "Modern minimal portfolio for a senior product designer named Sam Park."})
print(f"Enqueued: {run_id}")
asyncio.run(main())
Three steps, three modes:
| Step | Mode | Why |
|---|---|---|
plan | 0 (Bare) | Pure orchestration; Tier-1 LLM call on the worker. |
build_site | 3 (Full + CLI agent) | Claude Code does the actual work, with customer credentials injected into the sandbox env. |
summarise | 0 (Bare) | Tier-1 again. No sandbox cost for a one-line summary. |
Selecting mode per-step keeps sandbox costs where they are justified — see Execution modes — Mixing modes within a workflow.
5. Run it
python build_portfolio.py
Watch the admin panel's Runs view. You'll see:
planstep completes in ~500ms (Mode 0 — Tier-1 LLM call).build_siteentersrunning. The worker acquires a Docker sandbox, injects theportfolio-customer-XIdentity, and spawns Claude Code as a subprocess. Claude Code edits files in/workspace, thengit pushs to your repo usingGITHUB_TOKEN. This typically takes 5–15 minutes.summarisecompletes in ~500ms.
Check your GitHub repo — there's a portfolio site there. Check the admin Audit view — every secret-injection, cascade-resolution, and pool-reset event is recorded.
What happened
[control plane: admin server] [your terminal]
workflow_runs.status='pending' ←── enqueue
│
▼
[worker fleet: 1 worker] Tier-1: ORCHESTRATION_OPENAI_KEY
claims run; resolves Identity
│
▼
[Sagewai Agent on worker host]
plan step (Mode 0) — Tier-1 LLM
│
▼
[Docker sandbox] Tier-2: ANTHROPIC_API_KEY,
Identity env injected GITHUB_TOKEN
│
▼
[tool runner spawns Claude Code]
Claude Code → Anthropic API → /workspace
│
▼
[git push to your repo] artifact destination
│
▼
[control plane: admin server]
workflow_runs.status='completed'
Three things to note:
- Your Tier-2 credentials never reached the worker host. They were injected into the sandbox env directly from the Sealed profile and scrubbed on sandbox release.
- The control plane never executed a workflow step. It persisted, scheduled, and queried state — workers did the actual work.
- Mode selection was per-step. The cheap orchestration steps ran on the worker; only the CLI-agent step paid the sandbox-startup cost.
What to read next
Learn the model
- Architecture: Runtime topology — how a workflow run executes end-to-end.
- Architecture: Security tiers — Tier-1 vs Tier-2, the trust boundary, what Sagewai promises.
- Architecture: Execution modes — the five modes a step can run in, with the same portfolio-site example walked through.
- Architecture: Sandbox backends — Docker, Kubernetes, Lambda, and how to pick.
Go deeper on the SDK
- Minimal Setup — the no-sandbox, no-Identity flow if you want to start from a hello-world agent first.
- Agents — UniversalAgent, composition patterns.
- Workflows — durable workflows, approval gates.
- Sandboxing handbook — operator-level sandbox configuration (CLI flags, fallbacks, debugging).
Operate
- Self-Hosted Deployment — production install.
- Fleet Architecture — multi-worker topology.
- Hardware Requirements — sizing.