Dark Factory is an API-first coordination layer between humans, external agents, and downstream artifact systems.
Run the Next.js app locally against the shared Railway Postgres instance:
npm install
npm run devAdd a .env.local in the repo root with at least:
DATABASE_URL=postgresql://...
DARK_FACTORY_API_KEYS_JSON='[
{"key":"admin-local-1","role":"admin","label":"owner"},
{"key":"agent-memory-1","role":"agent","agent_id":"agent-memory","label":"memory-agent"}
]'Optional Agent Mail integration vars:
AGENT_MAIL_URL=https://mcp-agent-mail-production-ae0a.up.railway.app/mcp/
AGENT_MAIL_BEARER_TOKEN=...
AGENT_MAIL_WEB_URL=https://mcp-agent-mail-production-ae0a.up.railway.app
AGENT_MAIL_PROJECT_KEY=/absolute/path/to/prism-coord
AGENT_MAIL_AGENT_MAP_JSON='{"agent-memory":"AmberOtter"}'Or copy the checked-in example:
cp .env.example .env.localNotes:
- local development should use the public Postgres URL, not Railway's internal
postgres.railway.internalhostname - deployed Railway services should use internal service URLs/variable references
- startup validates
DATABASE_URL, API key JSON shape, and basic Agent Mail env coherence - the homepage now includes a hidden entrance: knock three times on the door to reveal the
/runsdashboard
flowchart LR
H[Humans] -->|create runs approve review| DF[Dark Factory API]
A1[External Agent: Memory] -->|heartbeat events task updates| DF
A2[External Agent: Knowledge] -->|heartbeat events task updates| DF
A3[External Agent: Content] -->|heartbeat events task updates| DF
DF -->|task state approvals handoffs| DB[(Postgres)]
DF -->|artifact references only| AR[Artifact Systems]
AR --> Git[Git Repos]
AR --> KB[Knowledge Base]
AR --> Pub[Publishing Channels]
flowchart TD
Admin[Admin or Trusted Operator] -->|issues bound API key| KeyReg[Key Registry]
KeyReg -->|agent key + bound agent_id| Agent[External Agent Runtime]
Agent -->|POST /agents/register-self| DF[Dark Factory API]
DF -->|upsert bound identity only| AgentReg[(Agent Registry)]
Admin -->|POST /agents/register| DF
DF -->|provision registry entry| AgentReg
sequenceDiagram
participant Human
participant DF as Dark Factory
participant Agent as External Agent
participant Store as Artifact Store
Human->>DF: Create workflow run
DF-->>Agent: Task becomes available
Agent->>DF: POST /agents/:id/heartbeat
Agent->>DF: POST /agents/:id/events (task.started)
Agent->>Store: Produce or update artifact
Agent->>DF: POST /artifacts
Agent->>DF: POST /agents/:id/events (task.completed)
Human->>DF: Approve or request revisions
- Activity broadcast implementation guide: docs/agent-activity-broadcast.md
- The landing page is intentionally product-facing; the operator surface begins at
/runs - Future runtime skill draft: docs/future-agent-runtime-skill-draft.md
- Reusable content-agent skill: skills/dark-factory-content-runtime/SKILL.md
- Minimal two-agent content workflow walkthrough: docs/minimal-content-workflow-e2e.md
The repo skill is stored in:
To make it available to another Codex agent, copy or symlink the whole skill folder into that agent's Codex skills directory:
mkdir -p "$HOME/.codex/skills"
cp -R skills/dark-factory-content-runtime "$HOME/.codex/skills/dark-factory-content-runtime"Or symlink it so updates in this repo stay live:
mkdir -p "$HOME/.codex/skills"
ln -s "$(pwd)/skills/dark-factory-content-runtime" "$HOME/.codex/skills/dark-factory-content-runtime"After that, a Codex session can trigger it by:
- naming the skill:
dark-factory-content-runtime - or asking for the matching workflow explicitly, for example:
- "check Agent Mail for Dark Factory content tasks and work the inbox-driven loop"
- "use the Dark Factory content runtime skill for this workflow"
What the agent still needs at runtime:
- access to the
prism-coordrepo/workspace - a bound Dark Factory API key
DATABASE_URLif it is running the local appAGENT_MAIL_URLAGENT_MAIL_BEARER_TOKEN- optionally
AGENT_MAIL_PROJECT_KEYandAGENT_MAIL_AGENT_MAP_JSON
Recommended setup for a working agent:
- install the skill into
~/.codex/skills - give the agent access to the
prism-coordworkspace - provide the bound API key and Agent Mail env vars
- tell the agent which workflow run or inbox/project it should operate on
The skill does not replace runtime provisioning. It standardizes behavior once the agent has access.
Every working agent should be provisioned with:
DARK_FACTORY_URLDARK_FACTORY_API_KEYAGENT_IDAGENT_MAIL_URLAGENT_MAIL_BEARER_TOKEN
Dark Factory auth uses:
- header:
x-df-api-key: <DARK_FACTORY_API_KEY>
The first call an agent should make is usually:
curl -X POST "$DARK_FACTORY_URL/api/v1/agents/register-self" \
-H "content-type: application/json" \
-H "x-df-api-key: $DARK_FACTORY_API_KEY" \
-d '{
"name":"memory-manager",
"description":"Research agent",
"type":"memory",
"capabilities":["memory.research"]
}'Important:
- give each agent only its own bound key
- do not give working agents the admin key
- the full
DARK_FACTORY_API_KEYS_JSONstays on theprism-coordservice, not inside every agent runtime
POST /api/v1/agents/registeris admin-only and should be treated as provisioningPOST /api/v1/agents/register-selfis for trusted self-upsert after a key has already been issued- self-registration must derive identity from the authenticated key binding, not request payload
All write requests to /api/v1/* (POST, PUT, PATCH, DELETE) require:
- header:
x-df-api-key: <your-key> - env var:
- preferred:
DARK_FACTORY_API_KEYS_JSON(multiple keys) - fallback:
DARK_FACTORY_API_KEY(single legacy key, admin)
- preferred:
If no keys are configured, write APIs return 503 and are effectively disabled.
export DARK_FACTORY_API_KEYS_JSON='[
{"key":"admin-local-1","role":"admin","label":"owner"},
{"key":"human-review-1","role":"human","label":"editor"},
{"key":"agent-memory-1","role":"agent","agent_id":"agent-memory","label":"memory-agent"},
{"key":"agent-knowledge-1","role":"agent","agent_id":"agent-knowledge","label":"knowledge-agent"}
]'Notes:
- Agent keys are bound to
agent_idfor writes under/api/v1/agents/:agentId/*. - Agent keys are blocked from endpoints outside their role policy.
admin:- can write all
/api/v1/*endpoints
- can write all
human:- can write:
/api/v1/tasks*,/api/v1/workflow-runs*,/api/v1/approvals*,/api/v1/artifacts,/api/v1/artifacts/:id/mark-approved,/api/v1/handoffs* - cannot write:
/api/v1/agents/register,/api/v1/agents/:id,/api/v1/agents/:id/heartbeat,/api/v1/agents/:id/events
- can write:
agent:- can write:
/api/v1/tasks*,/api/v1/artifacts,/api/v1/handoffs*,/api/v1/agents/:id/heartbeat,/api/v1/agents/:id/events - cannot write:
/api/v1/approvals*,/api/v1/workflow-runs*,/api/v1/agents/register,/api/v1/agents/:id - plus
agent_idbinding is enforced for/api/v1/agents/:id/*
- can write:
curl -X POST http://localhost:3000/api/v1/tasks \
-H "content-type: application/json" \
-H "x-df-api-key: admin-local-1" \
-d '{"title":"Draft post","task_type":"content.drafting"}'curl -X POST http://localhost:3000/api/v1/agents/agent-memory/heartbeat \
-H "content-type: application/json" \
-H "x-df-api-key: agent-memory-1" \
-d '{"status":"working","progress_pct":42}'Dark Factory mirrors task lifecycle updates into Agent Mail without making Agent Mail the source of truth for task state.
Current behavior:
- task
claim,start,block, andcompletesend a mirrored Agent Mail message startcan create file reservations whenfile_pathsare suppliedblockandcompleterelease file reservations for supplied pathsPOST /api/v1/agents/:agentId/eventsalso writes atask_eventsrow whentask_idis present
Conventions:
- workflow thread id:
run-<workflow_run_id> - message subject prefix:
[task:<task_id>] ... - reservation reason:
task:<task_id>
Current focus:
- validate the content workflow path end to end before expanding into software-delivery or PR-driven flows
- keep
prism-coordas the control plane and Agent Mail as the communication plane - use the current content templates and agent roles as the main dogfood scenario
Completed:
- Provision Railway Postgres and apply the initial schema
- Rename repo schema layout from
supabase/migrationstodb/migrations - Add seed data under
db/seeds - Build the first Postgres access layer under
lib/db - Implement DB-backed read routes for runs and tasks
- Implement authenticated write routes for agent registration, heartbeat, events, and task lifecycle transitions
- Persist
task_eventsduring task transitions - Mirror agent events into
task_eventswhentask_idis present - Deploy
prism-coordon Railway - Deploy
mcp-agent-mailon Railway with persistent mounted storage - Wire
prism-coordto Agent Mail with mirrored task messages and file reservations - Normalize Agent Mail thread ids and stable agent-name mapping
- Port the themed landing page assets and add the hidden door entrance to
/runs - Add workflow template listing and workflow run creation from the API
- Add a minimal workflow-run creation UI on
/runs - Materialize seeded workflow templates into tasks and task dependencies
- Implement handoff persistence and task-scoped handoff reads
- Add handoff creation and handoff history in the task drawer
- Add a dedicated task event/activity view in the task drawer
- Show Agent Mail thread summary, unread counts, and reservation conflicts on runs/tasks surfaces
- Add Agent Mail project/thread links to run and task surfaces
- Expand workflow run update flow with a run controls panel on
/runs - Expand workflow run creation beyond the minimal panel with initial status selection and template task previews
- Establish
owner_agent_keyas the current template-routing convention in seeded content templates - Add startup env validation and a checked-in
.env.example - Add automated tests for workflow creation, handoffs, task events, and task transition routes
- Draft the future inbox-driven agent runtime skill in docs/future-agent-runtime-skill-draft.md
- Turn the future agent runtime draft into a reusable repo skill at skills/dark-factory-content-runtime/SKILL.md
Next:
- Add richer task-level operator actions beyond the current read-heavy drawer and handoff form
- Evolve workflow template routing beyond the current
owner_agent_keyconvention into role/capability-aware routing - Add richer env validation around Railway/local mode assumptions if local onboarding stays rough
- Decide whether to keep the current best-effort Agent Mail fire-and-forget behavior or move mirroring into a domain service/queue
- Tighten Agent Mail auth/production hardening beyond the current bearer-token setup, using docs/agent-mail-hardening-notes.md as the baseline