Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
10e191f
feat: Substrate workspace-runtime adapter contract (fixture-backed)
oldsj Sep 22, 2026
e4bf2dd
fix: create_actor was missing -o json; add Substrate ActorTemplate ma…
oldsj Sep 22, 2026
032277f
docs: record the Substrate workspace-adapter spike
oldsj Sep 22, 2026
784a5b4
style: format substrate-workspace-adapter spike doc
oldsj Sep 22, 2026
cfed932
feat: prove the preview/HMR gate live; find the credential-injection gap
oldsj Sep 22, 2026
b2a4d23
feat: prove the dev-service gate live, including real policy enforcement
oldsj Sep 22, 2026
b7ffcbe
feat: build gate-5 live-agent infrastructure; block on safety classifier
oldsj Sep 22, 2026
4514cda
fix: repair Gate 5 workspace adapter harness
oldsj Sep 22, 2026
190b222
fix: correct Gate 5 rerun and router handling
oldsj Sep 23, 2026
a1d26b5
feat: add Phase 3 actor ingress boundary
oldsj Sep 23, 2026
5536f66
fix: preflight actor images and bound health probes
oldsj Sep 23, 2026
e362e95
feat: add Substrate egress tooling and native credential delivery
oldsj Sep 23, 2026
7db742b
feat: add native-agent credential delivery and startup
oldsj Sep 23, 2026
147e2a0
spike: run native turns headlessly and close out Phase 5 evidence
oldsj Sep 23, 2026
d9fc9e2
feat: add Substrate transport for native sessions
oldsj Sep 24, 2026
5d27646
feat: add local Substrate preview deployment
oldsj Sep 24, 2026
63c5f2b
feat(workspaces): add lifecycle state and controls
oldsj Sep 24, 2026
56cc7df
cutover: remove legacy runtimes and fence workspace deliveries
oldsj Sep 24, 2026
96716fe
feat(workspaces): add per-branch dev lifecycle
oldsj Sep 24, 2026
76dbad6
feat(auth): broker agent credentials and sign-in recovery
oldsj Sep 24, 2026
3f44399
feat(preview): add workspace previews and persistent shim processes
oldsj Sep 24, 2026
418bfb1
fix(workspaces): align shim auth and preview wake
oldsj Sep 24, 2026
9001ad5
fix(substrate): isolate sessions and fence actor credentials
oldsj Sep 24, 2026
b493ba5
docs: document the Substrate workspace architecture
oldsj Sep 24, 2026
b6537f5
spikes: remove Herdr-only Substrate assets
oldsj Sep 24, 2026
6f44fee
fix(preview): authenticate preview owners and bound proxying
oldsj Sep 24, 2026
9b66e0b
chore(substrate): run Substrate from the oldsj/substrate fork
oldsj Sep 24, 2026
71d014f
Make Substrate actors run non-root with the product template
oldsj Sep 24, 2026
f1080ff
Close the #73 review findings for Substrate workspaces
oldsj Sep 25, 2026
1b11b2c
Pin Substrate fork 0f9635ae and add live Substrate proofs
oldsj Sep 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,17 @@ DB_NAME=mainloop
DB_USER=mainloop
DB_PASSWORD=changeme

# Claude Code Credentials
# These will be automatically populated by 'make setup-claude-creds'
# CLAUDE_CREDENTIALS=<extracted-from-keychain>
# Substrate native-session routing
SUBSTRATE_ROUTER_ADDRESS=http://atenet-router.ate-system.svc.cluster.local:8081
SUBSTRATE_SHIM_SECRET_NAMESPACE=mainloop-control
# Map each provider to a pre-created actor and its shim token Secret.
SUBSTRATE_ACTOR_BINDINGS={"claude":{"atespace":"<atespace>","actor":"<claude-actor>","shim_token_secret_name":"<claude-shim-secret>"},"codex":{"atespace":"<atespace>","actor":"<codex-actor>","shim_token_secret_name":"<codex-shim-secret>"}}
# Substrate lifecycle control; empty values use the host's ambient config.
SUBSTRATE_KUBECONFIG=
SUBSTRATE_CONTEXT=
SUBSTRATE_ATESPACE=mainloop-workspaces
SUBSTRATE_ACTOR_TEMPLATE=mainloop-workspace
SUBSTRATE_CLI=kubectl-ate

# GitHub Container Registry
# Your GitHub username for pushing images to ghcr.io
Expand Down
5 changes: 0 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,6 @@ jobs:
context: .
tag: mainloop-frontend:test
build-args: VITE_API_URL=http://localhost:8081
- image: agent
file: claude-agent/Dockerfile
context: ./claude-agent
tag: mainloop-agent-controller:test

steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
1 change: 0 additions & 1 deletion .trunk/trunk.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ lint:
- backend/src/mainloop/config.py
- backend/src/mainloop/db/postgres.py
- backend/src/mainloop/services/github_pr.py
- claude-agent/server.py
# DL3008: Pinning apt versions causes build failures when versions are removed from repos
# Terrascan Docker checks have platform differences (Linux vs macOS) and false positives
# for multi-stage builds with --chown flags
Expand Down
3 changes: 1 addition & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ When documents differ, do not silently blend future design with current behavior
- Keep task lifecycle, agent activity, message delivery, user attention, workspace health, and publication state as separate concepts.
- Parallel work is limited by architectural cohesion, not just worker capacity. Resolve shared contracts and helpers before dispatching independent consumers.

The `claude-agent/` service and Claude Agent SDK paths are part of the existing implementation. The roadmap intends to replace them deliberately with native-agent integration; do not extend them as the new long-term architecture unless a compatibility change requires it.
Native Claude Code and Codex sessions run through the Substrate workspace adapter. Do not add the superseded worker or another workspace runtime.

## Project structure

Expand All @@ -33,7 +33,6 @@ backend/ FastAPI, DBOS workflows, PostgreSQL access, and orchestration
frontend/ SvelteKit 5 application and Playwright tests
models/ Shared Python/Pydantic models
packages/ui/ Shared frontend theme and UI package
claude-agent/ Existing Claude Agent SDK worker implementation
k8s/ Kubernetes bases and overlays
docs/specs/ User-visible behavior specifications
scripts/ Development and test automation
Expand Down
1 change: 0 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ Thank you for your interest in contributing to mainloop! This document provides
mainloop/
├── backend/ # Python FastAPI + DBOS workflows
├── frontend/ # SvelteKit + Tailwind v4
├── claude-agent/ # Claude Code CLI container
├── models/ # Shared Pydantic models
├── packages/ui/ # Design tokens + theme
└── k8s/ # Kubernetes manifests
Expand Down
117 changes: 7 additions & 110 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: help dev dev-stop dev-reset dev-logs dev-shell dev-legacy install clean lint lint-all fmt fmt-all setup-claude-creds setup-claude-creds-k8s build-backend build-frontend build-agent-controller build-all push-backend push-frontend push-agent-controller push-all build-all-parallel push-all-parallel deploy deploy-loop deploy-loop-all deploy-backend deploy-agent deploy-frontend-k8s deploy-manifests prod-reset kind-create kind-delete kind-load kind-secrets kind-deploy kind-reset kind-logs kind-shell test test-run test-reset test-ci debug-tasks debug-task debug-retry debug-logs debug-db
.PHONY: help dev dev-stop dev-reset dev-logs dev-shell dev-legacy install clean lint lint-all fmt fmt-all build-backend build-frontend build-all push-backend push-frontend push-all build-all-parallel push-all-parallel deploy deploy-loop deploy-loop-all deploy-backend deploy-frontend-k8s deploy-manifests prod-reset kind-create kind-delete kind-load kind-secrets kind-deploy kind-reset kind-logs kind-shell test test-run test-reset test-ci debug-tasks debug-task debug-retry debug-logs debug-db

# Load .env file if it exists
-include .env
Expand All @@ -11,7 +11,6 @@ GHCR_USER ?= yourusername
IMAGE_TAG ?= latest
BACKEND_IMAGE := $(GHCR_REGISTRY)/$(GHCR_USER)/mainloop-backend:$(IMAGE_TAG)
FRONTEND_IMAGE := $(GHCR_REGISTRY)/$(GHCR_USER)/mainloop-frontend:$(IMAGE_TAG)
AGENT_CONTROLLER_IMAGE := $(GHCR_REGISTRY)/$(GHCR_USER)/mainloop-agent-controller:$(IMAGE_TAG)

help: ## Show this help message
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
Expand All @@ -26,7 +25,7 @@ dev: ## Start dev environment with hot reload (DevSpace + Kind)
dev-stop: ## Stop DevSpace and purge resources
devspace purge --kube-context kind-$(KIND_CLUSTER_NAME) -n mainloop

dev-reset: ## Reset ALL data (database + task namespaces + restart backend)
dev-reset: ## Reset local database and restart the backend
@./scripts/kind/reset-data.sh

dev-clear-cache: ## Clear Vite cache (fixes stale HMR issues)
Expand Down Expand Up @@ -70,72 +69,6 @@ fmt-all: ## Format and fix all files
trunk fmt -a
trunk check -a -y

setup-claude-creds: ## Login to Claude inside Linux container, credentials saved to shared volume
@echo "=== Claude Container Login ==="
@echo "This will start a container where you can login to Claude."
@echo "Credentials will be saved to the 'claude-config' Docker volume."
@echo ""
@docker build -q -t mainloop-claude-agent ./claude-agent > /dev/null
@docker volume create claude-config > /dev/null 2>&1 || true
@docker rm -f claude-login-tmp > /dev/null 2>&1 || true
@docker run -d --entrypoint "" --name claude-login-tmp \
-v claude-config:/home/claude/.claude \
mainloop-claude-agent sleep 3600 > /dev/null
@echo "Container started with shared claude-config volume."
@echo "Run: claude login"
@echo "Complete the browser OAuth flow, then type 'exit'"
@echo ""
@docker exec -it claude-login-tmp bash; \
echo ""; \
echo "Checking credentials..."; \
if docker exec claude-login-tmp test -f /home/claude/.claude/.credentials.json; then \
echo "✓ Credentials saved to claude-config volume"; \
echo " All containers mounting this volume will have access."; \
else \
echo "⚠ No credentials found. Did you complete the login?"; \
fi; \
docker rm -f claude-login-tmp > /dev/null

setup-claude-creds-mac: ## Extract Claude credentials from macOS Keychain (Mac only)
@echo "Extracting Claude credentials from macOS Keychain..."
@CREDS=$$(security find-generic-password -s "Claude Code-credentials" -a "$(USER)" -w 2>/dev/null); \
if [ -z "$$CREDS" ]; then \
echo "Error: Claude credentials not found in Keychain."; \
echo "Make sure you're logged in to Claude Code on this Mac."; \
exit 1; \
fi; \
if [ -f .env ]; then \
echo "Updating CLAUDE_CREDENTIALS in .env (preserving other variables)..."; \
grep -v "^CLAUDE_CREDENTIALS=" .env > .env.tmp || true; \
mv .env.tmp .env; \
else \
echo "Creating .env file..."; \
touch .env; \
fi; \
echo "CLAUDE_CREDENTIALS=$$CREDS" >> .env; \
echo "✓ Claude credentials updated in .env"

setup-claude-creds-k8s: ## Push Claude credentials from Docker volume to 1Password for k8s
@echo "Extracting credentials from Docker volume and pushing to 1Password..."
@if ! command -v op >/dev/null 2>&1; then \
echo "Error: 1Password CLI (op) not found."; \
echo "Install it: brew install --cask 1password-cli"; \
exit 1; \
fi; \
docker run --rm -v claude-config:/config:ro alpine cat /config/.credentials.json > /tmp/claude-creds.json 2>/dev/null; \
if [ ! -s /tmp/claude-creds.json ]; then \
echo "Error: No credentials in claude-config volume"; \
echo "Run 'make setup-claude-creds' first"; \
rm -f /tmp/claude-creds.json; \
exit 1; \
fi; \
echo "Creating/updating claude-credentials item in kubernetes vault..."; \
op item get claude-credentials --vault kubernetes >/dev/null 2>&1 && \
op item delete claude-credentials --vault kubernetes || true; \
op document create /tmp/claude-creds.json --title=claude-credentials --vault=kubernetes >/dev/null; \
rm -f /tmp/claude-creds.json; \
echo "✓ Claude credentials pushed to 1Password vault 'kubernetes'"

# Backend commands
backend-dev: ## Run backend in development mode
cd backend && uv run uvicorn mainloop.api:app --reload --host 0.0.0.0 --port 8000
Expand All @@ -151,36 +84,28 @@ build-backend: ## Build backend Docker image
build-frontend: ## Build frontend Docker image
docker build -f frontend/Dockerfile -t $(FRONTEND_IMAGE) .

build-agent-controller: ## Build agent controller Docker image
docker build -f claude-agent/Dockerfile -t $(AGENT_CONTROLLER_IMAGE) ./claude-agent

build-all: build-backend build-frontend build-agent-controller ## Build all Docker images
build-all: build-backend build-frontend ## Build all Docker images

push-backend: build-backend ## Push backend to GHCR
docker push $(BACKEND_IMAGE)

push-frontend: build-frontend ## Push frontend to GHCR
docker push $(FRONTEND_IMAGE)

push-agent-controller: build-agent-controller ## Push agent controller to GHCR
docker push $(AGENT_CONTROLLER_IMAGE)

push-all: push-backend push-frontend push-agent-controller ## Push all images to GHCR
push-all: push-backend push-frontend ## Push all images to GHCR

# Parallel build + push (much faster)
build-all-parallel: ## Build all Docker images in parallel
@echo "Building all images in parallel..."
@docker build -f backend/Dockerfile -t $(BACKEND_IMAGE) . & \
docker build -f frontend/Dockerfile -t $(FRONTEND_IMAGE) . & \
docker build -f claude-agent/Dockerfile -t $(AGENT_CONTROLLER_IMAGE) ./claude-agent & \
wait
@echo "All builds complete"

push-all-parallel: build-all-parallel ## Build and push all images in parallel
@echo "Pushing all images in parallel..."
@docker push $(BACKEND_IMAGE) & \
docker push $(FRONTEND_IMAGE) & \
docker push $(AGENT_CONTROLLER_IMAGE) & \
wait
@echo "All pushes complete"

Expand All @@ -190,7 +115,6 @@ deploy: push-all-parallel ## Full deployment to k8s (parallel builds + pushes)
kubectl apply -k k8s/apps/mainloop/overlays/prod --server-side --force-conflicts
@echo "Restarting Kubernetes deployments in parallel..."
@kubectl rollout restart deployment/mainloop-backend -n mainloop & \
kubectl rollout restart deployment/mainloop-agent-controller -n mainloop & \
kubectl rollout restart deployment/mainloop-frontend -n mainloop & \
wait
@echo "Rollouts triggered"
Expand All @@ -203,7 +127,7 @@ deploy-loop: ## Pull and deploy every 10 seconds
done

deploy-loop-all: ## Watch all and redeploy everything (old behavior)
watchexec --poll 1000 -w backend -w frontend/src -w k8s -w models -w claude-agent \
watchexec --poll 1000 -w backend -w frontend/src -w k8s -w models \
-e py,ts,svelte,yaml,toml,Dockerfile \
-i 'test*' -i '*_test.py' -i 'tests/' -i '__pycache__/' -i '.pytest_cache/' -i 'scripts/' \
--on-busy-update restart \
Expand All @@ -215,11 +139,6 @@ deploy-backend: ## Build, push, and restart backend only
docker push $(BACKEND_IMAGE)
kubectl rollout restart deployment/mainloop-backend -n mainloop

deploy-agent: ## Build, push, and restart agent controller only
docker build -f claude-agent/Dockerfile -t $(AGENT_CONTROLLER_IMAGE) ./claude-agent
docker push $(AGENT_CONTROLLER_IMAGE)
kubectl rollout restart deployment/mainloop-agent-controller -n mainloop

deploy-frontend-k8s: ## Build, push, and restart frontend only (k8s version)
docker build -f frontend/Dockerfile -t $(FRONTEND_IMAGE) .
docker push $(FRONTEND_IMAGE)
Expand All @@ -230,13 +149,8 @@ deploy-manifests: ## Apply k8s manifests only (no image builds)

PROD_CONTEXT ?= admin@internal-01

prod-reset: ## Reset prod database + task namespaces + restart backend
prod-reset: ## Reset prod database + restart backend
@echo "=== Using context: $(PROD_CONTEXT) ==="
@echo "=== Cleaning up k8s task namespaces ==="
@for ns in $$(kubectl --context $(PROD_CONTEXT) get ns -o name 2>/dev/null | grep "^namespace/task-" | cut -d/ -f2); do \
echo "Deleting namespace: $$ns"; \
kubectl --context $(PROD_CONTEXT) delete ns "$$ns" --wait=false 2>/dev/null || true; \
done
@echo "=== Deleting CNPG Database CR ==="
kubectl --context $(PROD_CONTEXT) delete database mainloop-db-database -n mainloop --wait=true
@echo "=== Recreating Database CR ==="
Expand Down Expand Up @@ -293,27 +207,14 @@ test-k8s: kind-create kind-load kind-secrets kind-deploy ## Start local K8s test

test-loop: ## Watch for changes and auto-redeploy to Kind
@echo "Starting Kind deploy loop (Ctrl+C to stop)..."
@echo "Watching: backend/, frontend/src/, claude-agent/"
@echo "Watching: backend/, frontend/src/"
@trap 'kill 0' INT; \
watchexec -w backend/src -w models -e py \
--on-busy-update restart -- bash -c 'make kind-load && make kind-deploy' & \
watchexec -w frontend/src -e ts,svelte,css \
--on-busy-update restart -- bash -c 'make kind-load && make kind-deploy' & \
watchexec -w claude-agent -e py \
--on-busy-update restart -- bash -c 'make kind-load && make kind-deploy' & \
wait

# E2E Testing
test-k8s-components: ## Test K8s namespace/secret creation (quick)
cd backend && uv run python scripts/test_k8s_components.py

test-k8s-job: ## Test K8s job creation (creates a real job)
cd backend && uv run python scripts/test_k8s_components.py --job

test-worker-e2e: ## Run full worker E2E test (disabled; ENABLE_E2E=1 to opt in)
@./scripts/e2e-guard.sh
cd backend && REPO_URL="$(or $(REPO_URL),https://github.com/oldsj/mainloop)" uv run python scripts/test_worker_e2e.py

# =============================================================================
# Testing (DevSpace + Playwright)
#
Expand All @@ -340,10 +241,6 @@ test-reset: dev-reset ## Alias for dev-reset

test-ci: ## Run tests with legacy kind scripts (disabled; ENABLE_E2E=1 to opt in)
@./scripts/e2e-guard.sh
@if [ -z "$(CLAUDE_CODE_OAUTH_TOKEN)" ]; then \
echo "Error: CLAUDE_CODE_OAUTH_TOKEN not set"; \
exit 1; \
fi
@if ! kind get clusters 2>/dev/null | grep -q "^$(KIND_CLUSTER_NAME)$$"; then \
$(MAKE) kind-create; \
fi
Expand Down
25 changes: 12 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ You (phone/laptop)
▼
┌─────────────────────────────────────────────────────┐
│ Main Thread │
│ Claude with spawn_session tool │
│ Native Claude Code session in Substrate │
│ │
│ user@mainloop$ research X ← inline sessions │
│ ├── [research X] thinking... ← threaded reply │
Expand All @@ -33,20 +33,20 @@ You (phone/laptop)
└──────────────┘ └──────────────┘
```

- **Main thread**: One continuous conversation — sessions spawn inline and surface results back
- **Sessions**: Background AI work with their own conversations; appear as colored threads in your timeline
- **Main thread**: One continuous native conversation; delegated sessions surface results back
- **Sessions**: Native Claude Code or Codex work with their own conversations; appear as colored threads in your timeline
- **Notifications**: Slack-style thread replies notify you when sessions need attention or complete
- **Persistence**: Conversations and sessions survive restarts via compaction + [DBOS](https://docs.dbos.dev/)
- **Persistence**: Mainloop stores conversations, delivery records, and workspace lifecycle state in PostgreSQL; native history remains with the provider CLI in Substrate
- **Runtime isolation**: Substrate workspaces use gVisor actors. The pinned fork honors the
agent image's non-root UID `10001`; microVM isolation is deferred.

## Quick Start

```bash
# Copy example environment file and configure
cp .env.example .env
# Edit .env with your GitHub username (GHCR_USER) and domains

# Optional: authenticate the current Claude integration
make setup-claude-creds
# Set the Substrate router, actor bindings, and shim Secret names.
# Keep provider credentials in Mainloop's configured control-plane Secrets; actors receive synthetic placeholders only.

# Start all services
make dev
Expand All @@ -65,7 +65,6 @@ The Kubernetes manifests under `k8s/apps/mainloop/` provide reusable bases and e
mainloop/
├── backend/ # Python FastAPI + DBOS workflows
├── frontend/ # SvelteKit + Tailwind v4 (mobile-first responsive)
├── claude-agent/ # Claude Code CLI container
├── models/ # Shared Pydantic models
├── packages/ui/ # Design tokens + theme.css
└── k8s/ # Kubernetes manifests
Expand All @@ -92,20 +91,20 @@ mainloop/

## Agent Workflow

Agents are sessions spawned for development tasks. Each agent gets its own K8s namespace for isolated iteration.
Agents are native sessions spawned for development tasks. Mainloop records the session and its deliveries; the native CLI runs in the Substrate actor selected by the configured provider binding.

```text
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Spawn │────►│ Work │────►│ PR │────►│ Close │
│ (main) │ │ (k8s ns) │ │ (GitHub) │ │ (summary) │
│ (main) │ │(Substrate) │ │ (GitHub) │ │ (summary) │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
▲ │
└───────────────────┘
check in / spawn more
```

1. **Spawn** - Main thread creates agent for a task
2. **Work** - Agent iterates in its own K8s namespace (build, test, debug)
2. **Work** - A native Claude Code or Codex session runs in its configured Substrate actor
3. **PR** - Agent creates and merges GitHub PR when ready
4. **Close** - Agent posts summary back to main thread

Expand All @@ -121,7 +120,7 @@ You stay in main thread, checking in on agents and spawning new ones as needed.

**Guides**:

- [Architecture](docs/architecture.md) - System design and data flow
- [Architecture](docs/architecture.md) - Substrate workspaces, delivery, previews, and credentials
- [Contributing](CONTRIBUTING.md) - Local setup, development commands, and contribution guidance

## License
Expand Down
14 changes: 12 additions & 2 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Claude Agent (optional - for direct API access)
ANTHROPIC_API_KEY=your-api-key-here
# Native sessions connect to deployment-provided Substrate actors.
SUBSTRATE_ROUTER_ADDRESS=http://atenet-router.ate-system.svc.cluster.local:8081
SUBSTRATE_SHIM_SECRET_NAMESPACE=mainloop-control
# Map each native provider to its actor and shim token Secret name.
SUBSTRATE_ACTOR_BINDINGS={"claude":{"atespace":"<atespace>","actor":"<claude-actor>","shim_token_secret_name":"<claude-shim-secret>"},"codex":{"atespace":"<atespace>","actor":"<codex-actor>","shim_token_secret_name":"<codex-shim-secret>"}}

# Substrate lifecycle control. Leave empty to use the host's ambient config.
SUBSTRATE_KUBECONFIG=
SUBSTRATE_CONTEXT=
SUBSTRATE_ATESPACE=mainloop-workspaces
SUBSTRATE_ACTOR_TEMPLATE=mainloop-workspace
SUBSTRATE_CLI=kubectl-ate

# Server
HOST=0.0.0.0
Expand Down
Loading
Loading