Octra is a multi-agent AI orchestrator that builds software projects using a Boss → Manager → Worker pipeline. Every project is snapshotted into the Nix store, enabling instant rollback and zero-storage recovery.
Full API specification for custom clients →
docs/spec/
User → API Gateway → Boss (architect)
├── Manager (backend) → Worker × N
├── Manager (frontend) → Worker × N
└── Manager (devops) → Worker × N
↓
Agents Service → Claude / Gemini / GPT / ...
Boss plans architecture, spawns managers, validates output, pushes to GitHub.
Managers review and orchestrate workers.
Workers generate code inside Nix-isolated environments — either via AI or real tool scaffolding.
Octra's core principle: AI is a genius but untrustworthy brain — the system is a rigid shell that controls it.
AI tends to overcomplicate, add features no one asked for, hallucinate APIs, and deviate from the task. Octra does not fight this — it contains it:
- Divide and conquer — a single complex task is broken into many small, narrow AI calls (Boss → Manager → Worker). Each call answers exactly one question, with no room for creativity.
- JSON-only responses — every prompt demands structured output. AI does not narrate, justify, or explore — it answers precisely and moves on.
- Every step is validated — Boss validates the final result, Manager reviews worker output, failed commands are retried with error context. No output is trusted without verification.
- No system prompt roleplay — AI does not need to "act like a senior engineer." It needs to follow instructions exactly and produce parseable results.
- Determinism over creativity — low temperatures, constrained output formats, explicit rules for every decision. Predictability is more valuable than cleverness.
- Context as a resource, not a story — project context is stored as structured entries with scopes and importance flags, not as conversation history. AI does not "remember" — it is told what it needs to know.
This is not a limitation — it is the feature. Octra treats AI as a reliable executor within a controlled system, not as an autonomous agent.
Octra can scaffold real projects using native toolchains inside nix develop:
setupProject() → FlakeBuilder(techStack) → ToolExecutor(generateViaTools)
├── npm init / cargo init / flutter create / …
├── AI generation fallback if Nix/tool unavailable
└── git status --porcelain to detect created files
- FlakeBuilder generates
flake.nixwith Nix packages for 20+ tech stacks - ToolExecutor runs real scaffolding commands (
npm install,cargo init,composer create-project, etc.) - Graceful fallback to AI generation if Nix or tool is unavailable
When a scaffolding command fails (non-zero exit), its output is fed back to the AI for automatic fixing:
Worker generates code → runs commands → exit code ≠ 0
↓
AI receives error + current files → returns fixed files
↓
patched files written to disk
Only errors trigger the fix (successful command output is ignored). Each failed command gets one fix attempt.
Progress updates from the Boss → Manager → Worker pipeline are delivered via a buffered channel (256 slots) with blocking sends — no updates are dropped. Previously used non-blocking sends with select default:, which silently dropped file-write progress when the channel was full.
Octra ships with a registry of structured tool guides — one file per tool, organized by language ecosystem:
pkg/guids/
├── core/ # Guide type, registry, formatting
├── golang/ # go
├── rust/ # cargo
├── node/ # npm
├── python/ # pip
├── java/ # maven, gradle, kotlin/
├── php/ # composer, artisan
├── flutter/ # flutter
├── cpp/ # cmake
├── dotnet/ # dotnet
├── elixir/ # mix
├── haskell/ # cabal
├── zig/ # zig
├── swift/ # swiftpm
└── ruby/ # bundler, gem
Each guide contains structured commands, Nix packages, and project structure — injected into AI prompts to prevent hallucinated commands and save tokens.
Octra maintains per-project context with three visibility scopes, stored in Redis (fast cache) and PostgreSQL (permanent storage):
┌──────────────────┐
│ AI Agent returns │
│ JSON with │
│ "context" field │
└──────┬───────────┘
↓
┌───────────────────────────────┐
│ ContextService.SaveFromAI() │
│ ┌──────────┐ ┌────────────┐ │
│ │ Postgres │ │ Redis │ │
│ │ (always) │ │ (5min TTL) │ │
│ └──────────┘ └────────────┘ │
└───────────────────────────────┘
↓
┌───────────────────────────────┐
│ GetForPrompt() → injects │
│ into Boss/Manager/Worker │
└───────────────────────────────┘
Three scope levels:
| Scope | Visibility | Set by | Lifecycle |
|---|---|---|---|
global |
All agents in project | Boss/Manager | Persists forever |
team |
All workers under one manager | Manager | Until manager finishes |
individual |
Single agent | Any agent | Per agent session |
How context flows:
- Boss sends prompt → AI returns JSON with optional
"context": {"scope": "global", "type": "global_rule", "content": "..."} - ContextService saves to Postgres + Redis, then injects into all subsequent agent prompts
- Manager and Worker also receive and can create context (team/individual scopes)
- Before every AI call,
GetForPrompt()assembles all relevant context into a formatted block
Automatic cleanup:
- Messages capped at ~20 (adaptive: 40 if avg length <200 chars, 30 if <500 chars)
- When limit exceeded, the 5 oldest non-important messages are removed (sawtooth pattern)
- Global rules live forever unless user says
"forget" - If user/AI sends
"content": "forget"→ all matching entries are markedforgotten - Redis entries expire after 5 minutes, reloaded from Postgres on next access
Key files:
| File | Purpose |
|---|---|
internal/service/context/response.go |
AI context flag parsing, forget detection |
internal/service/context/postgres.go |
Permanent storage, adaptive cleanup |
internal/service/context/redis.go |
TTL cache (5 min), cache-aside pattern |
internal/service/context/service.go |
Coordinator, SaveFromAI, GetForPrompt |
pkg/models/context_entry.go |
GORM model (project_id, scope, type, content, …) |
Octra's workers use the Group Chat pattern (Microsoft Agent Framework) for true multi-agent collaboration:
Round 1: all agents generate concurrently (AllAtOnceSelector)
Worker A ──→ Message{files}
Worker B ──→ Message{files}
Worker C ──→ Message{files}
↓ broadcast
Round 2: each agent sees full conversation → can refine based on peer work
Worker A ──→ sees B+C files → improves
Worker B ──→ sees A+C files → improves
Worker C ──→ sees A+B files → improves
↓ termination condition
git add . + git commit
How agents "check the chat":
Every agent's Process(ctx, conv) receives the full Conversation with all messages and files. WorkerAgent.buildChatContext() extracts peer-generated files and content, injects them into the AI prompt — workers literally see what others built and can adapt.
Key components:
| File | Purpose |
|---|---|
internal/service/groupchat/types.go |
Agent, Message, Conversation, SpeakerSelector interfaces |
internal/service/groupchat/orchestrator.go |
Orcherator: registration, concurrent rounds, termination |
internal/service/groupchat/selection.go |
RoundRobin, AllAtOnce, RoleBased, AgentBased selectors |
internal/service/rules/worker/agent.go |
WorkerAgent implementing groupchat.Agent with AI generation |
Octra ships with a Skill Warehouse — a registry of atomic skill fragments that replace monolithic skill files. Instead of sending entire skill documents to every AI call, the system picks only the relevant fragments:
Traditional skills (7 large .md files) were broken into 42 atomic fragments (3-6 lines each):
internal/skills/fragments/
├── backend-api.md, backend-arch.md, backend-data.md, backend-security.md, ...
├── frontend-component.md, frontend-state.md, frontend-accessibility.md, ...
├── devops-cicd.md, devops-containers.md, devops-iac.md, ...
├── research-intent.md, research-sources.md, research-triangulate.md, ...
├── presentation-slide.md, presentation-story.md, presentation-visual.md, ...
├── proxy-types.md, proxy-headers.md, proxy-security.md, ...
└── vpn-protocol.md, vpn-crypto.md, vpn-network.md, ...
warehouse := skills.NewWarehouse()
warehouse.Catalog() // всё что есть на складе
warehouse.CatalogFiltered("Backend") // только указанные категории
warehouse.Search(role, tech, task, 3) // top-3 фрагмента по релевантности
warehouse.SearchFiltered(role, tech, task, 3, "Backend") // с фильтром по категории
warehouse.Select(slugs...) // только эти фрагменты → текст для промптаThe Boss can pass skill_categories in task metadata — then:
- Boss sees only those categories in the catalog (via
CatalogFiltered) - Manager searches fragments only in those categories (via
SearchFiltered) - Worker receives only the matching skills
Example:
// task metadata
{ "skill_categories": "Backend, Frontend" }Without the flag — all skills as before (backward compatibility).
After deciding worker roles, the Manager queries the Warehouse:
Manager → warehouse.Search("backend go", "go", taskDescription, 3)
→ ["backend-api", "backend-data", "backend-security"]
→ WorkerRole.SelectedSkillSlugs = ["backend-api", "backend-data", "backend-security"]
The Worker then resolves only those slugs via warehouse.Select(slugs...), injecting ~15-30 lines of targeted guidance instead of a full 100+ line skill file.
Drop a new .md file in internal/skills/fragments/ with frontmatter:
---
name: My New Skill
slug: my-skill
area: MyArea
keywords: relevant, keywords, here
tech: go, python
weight: 3
---
Short guidance text (3-6 lines).The Warehouse discovers it automatically via //go:embed.
| File | Purpose |
|---|---|
internal/skills/warehouse.go |
Warehouse registry, catalog, search, select, category filter |
internal/skills/fragments/*.md |
42 atomic skill fragments |
internal/skills/skills.go |
Legacy skill system (backward compat) |
internal/service/rules/worker/skill.go |
buildSkillContext() — fragment resolution entry point |
internal/service/rules/manager/assign.go |
Manager selects fragments per worker via Warehouse |
Octra can automatically publish generated projects to GitHub and create pull requests for issue-linked tasks. The behavior is configured in the Settings panel via two toggles:
| Toggle | Meta flag | Effect |
|---|---|---|
| Publish new projects to GitHub | publish_repositories |
Creates a new GitHub repository and pushes the generated code |
| Create pull requests | create_pull_requests |
Creates a PR for issue-linked tasks (pushes code to a branch in any case) |
When Octra targets a GitHub issue but the bot account lacks write permission, it automatically forks the repository under the bot's account:
Octra detects issue URL
│
├── Has write permission? ──→ clone original → push branch → PR
│
└── No write permission
│
├── CheckWritePermission(owner/repo) → false
├── ForkRepository(owner/repo) → fork created
├── WaitForkReady → clone fork
├── AddUpstream → SyncWithUpstream
└── Push to fork → PR from forkOwner:branch → owner:base
Cross-repo PRs use the head: "forkOwner:branchName" format. Fork info (github_fork_owner, github_fork_clone_url) is persisted in task metadata for subsequent runs — no re-fork on repeat.
If the same issue URL is submitted again, Octra detects the previous task and reuses its fork:
detectGitHubIssueTask()
│
└── findExistingTaskByIssue(issueURL)
│
├── Found in DB (status=done, nix_store_path != '')
│ │
│ ├── Read github_fork_owner from previous task's Meta
│ ├── Clone fork directly (skip permission check + fork creation)
│ ├── GetIssueCommentsSince(previousTask.UpdatedAt)
│ └── Inject "NEW COMMENTS" section into AI prompt
│
└── Not found → standard flow (permission check → fork → clone)
This ensures repeat runs on the same issue are fast (no re-fork) and incorporate user feedback from new comments.
Settings (UI) → meta.publish_repositories / meta.create_pull_requests
↓
task.go → pushToGitHub(ctx, task, projectPath, issueTarget, meta)
↓
github.go — checks flags and decides:
publish_repositories=false → skip repo creation
create_pull_requests=false → push branch only, skip PR
↓
WebSocket response → frontend adds GitHubNode
repoUrl → repository link
pullRequestUrl → PR link (displayed as "GitHub PR" node)
- If both toggles are off (integration connected but disabled) — no GitHub node is added to the canvas
- If a pull request is created — the node shows "GitHub PR" as role and opens the PR URL on click
- If a repository is published — the node shows "GitHub" as role and opens the repo URL on click
- The click handler respects
prUrloverrepoUrlwhen both are present
New issue: setupProject() → generate flake.nix → AI generates code → nix-store --add → cleanup
↓
Repeat issue: findExistingTask() → clone fork → AI with new comments → nix-store --add → cleanup
Each project:
- Gets a
flake.nixat creation time (auto-generated per tech stack) - Is built by AI agents inside the working directory
- On completion: snapshotted to
/nix/store/vianix-store --add - Working directory is removed (zero disk waste)
RestoreProject(taskID)recovers the project from the Nix store instantly- Repeat runs on the same GitHub issue detect the previous snapshot and skip the fork step — only new comments are fetched and fed to AI
This means projects take zero space when idle but can be restored at any time.
| Service | Stack | Role |
|---|---|---|
orchestrator |
Go, gRPC | Boss → Manager → Worker pipeline, Group Chat orchestration, Skill Warehouse, Nix snapshots, tool scaffolding, command auto-fix |
agents |
Go, gRPC | AI provider proxy (Claude, Gemini, GPT, DeepSeek, Ollama/custom, …) — supports OpenAI-compatible streaming and non-streaming |
apigateway |
Go, Gin, WebSocket | HTTP/WS → gRPC bridge |
user |
Go, Gin | Auth, subscriptions, custom providers |
frontend/web |
React, Vite, Electron | Interactive canvas + chat UI |
Run with NixOS:
{
imports = [ octra.flake.nixosModules.octra-orchestrator ];
services.octra-orchestrator = {
enable = true;
environment.DB_DNS = "postgres://...";
};
}docker compose up -d --buildOpen http://localhost, describe your task, Octra builds it.
Project is cached in Nix store — recover anytime via RestoreProject(taskID).
Octra: describe once, rebuild forever.