Skip to content

ashokraj2011/singularity-platform

Repository files navigation

Singularity Platform

License: MIT Status: Active development Services: 11+

An enterprise AI-agent platform composed of independently-deployable applications: identity, agent registry, prompt composition, LLM cost optimization, workflow orchestration, an MCP execution engine with embedded LLM gateway, a federated lookup + receipt + event-bus platform layer, and a unified portal that wraps them all.

Published as a monorepo: https://github.com/ashokraj2011/singularity-platform


Quickstart — clone to demo in ~5 minutes

Prerequisites

  • Docker Desktop (Compose v2)
  • git, curl, psql (optional, for ad-hoc inspection)
  • Ports free: 3000, 5174, 5175, 5180, 7100, 8000-8003, 8080, 8100-8101, 8500, 5432, 5433, 5434, 5436, 9000-9001
  • ~6 GB free RAM for the full stack

1. Clone

git clone https://github.com/ashokraj2011/singularity-platform.git
cd singularity-platform

2. Bring up — one command, three stacks

./singularity.sh up

This brings up:

  • Master stack (19 services): IAM + agent-and-tools (4 svcs) + context-fabric (4 svcs) + workgraph (api + web + pg + minio) + mcp-server-demo + portal + user-and-capability
  • Pseudo-IAM (port 8101): the SPA's auth authority — accepts any email/password, mints a JWT signed with the same secret as workgraph-api
  • Audit & governance ledger (port 8500): the cross-service event ledger every producer fires into

First boot pulls images + builds web bundles. Wait ~3–5 minutes. Tail with ./singularity.sh logs workgraph-api -f if you want to watch.

Need to bring up just one piece? ./singularity.sh up <service-name> works for the master-stack services (run ./singularity.sh ls for the list). The two side stacks (pseudo-iam, audit-governance) only come up via the no-arg form.

3. Seed the agent-and-tools DB

( cd agent-and-tools/apps/agent-runtime \
  && DATABASE_URL="postgresql://postgres:singularity@localhost:5432/singularity" \
     npx prisma db seed )

Lands 9 agent templates (4 locked common baselines + 5 derived), 5 prompt profiles, 7 layers, 4 tools, 1 default capability.

4. One-line smoke check

for u in \
  "http://localhost:8101/health" \
  "http://localhost:8500/health" \
  "http://localhost:7100/health" \
  "http://localhost:8000/health" \
  "http://localhost:8080/health" \
  "http://localhost:3000/api/runtime/agents/templates?scope=common&limit=3" \
  "http://localhost:5174/"; do
  printf "%-65s %s\n" "$u" "$(curl -s -o /dev/null -w '%{http_code}' $u)"
done

You should see 200 for all seven.

5. The demo path — five clicks, five "wow" moments

Step URL What to show
1. Login http://localhost:5174 Auto-login fires through pseudo-IAM. Mention: "any user, anywhere — same JWT secret as our IAM, swap the env var to point at real IAM"
2. Agent Studio http://localhost:3000/agent-studio → pick the seeded capability from the dropdown Show the four Locked common baselines (Architect / Developer / QA / Governance), click Derive → on one, name it. Mention: "derived agents inherit prompt profile + tool policy, become editable by capability owners, audit-gov captures agent.template.derived"
3. Run a workflow localhost:5174/runs → click Run a Workflow → pick "Business Initiative Delivery" → start The new run lands in /runs/<id>. Open a HUMAN_TASK node, attach a file, click Complete. Workflow advances.
4. Run Insights Click the green Insights → pill at the top of the run viewer The M24 dashboard — total duration, per-step Gantt with precise timing (startedAt/completedAt columns), artifacts list, cost-by-model, full audit timeline keyed to the run. Mention: "every step duration is authoritative, not inferred"
5. Governance & cost http://localhost:3000/audit and http://localhost:3000/cost Cross-service ledger. Show the recent agent.template.derived, cf.execute.completed, tool.execution.success, llm.call.completed rows. Then /cost for $$ + tokens, with model breakdown. Mention: "all four producers — mcp-server, workgraph-api, tool-service, context-fabric, agent-runtime — fire fire-and-forget events here; pre-flight budget/rate-limit checks happen inline."

6. Optional polish for the demo

  • Set a tight budget then watch DENIED:
    curl -s -X POST http://localhost:8500/api/v1/governance/budgets \
      -H 'content-type: application/json' \
      -d '{"scope_type":"capability","scope_id":"<cap-id>","period":"day","tokens_max":1}'
    Re-run any AGENT_TASK on that capability — status:DENIED returns instantly, no LLM dispatch. Open /audit → see governance.denied event.
  • MCP smoke (slick because it's the same call workflows make under the hood):
    curl -sS -X POST http://localhost:7100/mcp/invoke \
      -H 'authorization: Bearer demo-bearer-token-must-be-min-16-chars' \
      -H 'content-type: application/json' \
      -d '{"runContext":{"traceId":"t-demo","runId":"r-demo","capabilityId":"c-demo"},"message":"hi","tools":[]}'
  • Insights for a workflow that calls an LLM: design a workflow with an AGENT_TASK, point it at a derived agent, run. Insights will populate cost_usd + tokens + model breakdown for real.

7. Tear down

./singularity.sh down     # stop all three stacks, keep data volumes
./singularity.sh nuke     # stop + WIPE all data volumes (asks for confirmation)

URLs cheat sheet (print these)

Workgraph SPA            http://localhost:5174    runs, designer, insights
Agent / Tools SPA        http://localhost:3000    Agent Studio, /audit, /cost
Singularity Portal       http://localhost:5180    branded wrapper around all of it
User & Capability SPA    http://localhost:5175    IAM admin

Workgraph API            http://localhost:8080
Agent Runtime API        http://localhost:3003
Tool Service API         http://localhost:3002
Prompt Composer API      http://localhost:3004
Context Fabric API       http://localhost:8000
MCP Server               http://localhost:7100
Pseudo-IAM               http://localhost:8101
Audit & Governance API   http://localhost:8500

Known gotchas (fix before the demo)

  1. First boot of agent-runtime fails seed if pgvector extension isn't created. The compose's at-postgres is a pgvector image so this is usually OK; if you see type "vector" does not exist after a force-reset:
    docker exec agentandtools-postgres psql -U postgres -d singularity -c "CREATE EXTENSION IF NOT EXISTS vector;"
  2. Token errors after a long idle: pseudo-IAM signs 24h JWTs. If your tab sat overnight, hard-refresh localhost:5174 and let auto-login mint a fresh one.
  3. Port collisionslsof -i :5174 if the workgraph SPA won't start; another stack from a previous demo might still be holding it.

The narrative to lead with: "Singularity is a governed agent platform — every agent is rooted in a locked baseline, every workflow run is observable end-to-end, and every LLM call is gated against a budget."


Bare-metal alternative — single Postgres, no Docker

For dev machines that already have Postgres and don't want Docker. Focused on the demo path; skips real IAM (uses pseudo-IAM), the three optional context-fabric siblings, and MinIO (file uploads will fail but everything else works).

Just run the script (recommended)

bin/bare-metal.sh up <db_user> [db_password] [db_host] [db_port]
bin/bare-metal.sh smoke      # curl every /health endpoint
bin/bare-metal.sh status     # list running PIDs
bin/bare-metal.sh logs workgraph-api    # tail one service
bin/bare-metal.sh down       # stop everything + free ports

Idempotent — re-runs of up skip installs and DB creation if they already happened, just re-boots. Defaults: db_password from $PGPASSWORD env or postgres, db_host=localhost, db_port=5432.

The manual recipe below is what the script does under the hood — useful if you want to step through it or diverge.

1. Postgres prep — one shot

Adjust user/password to match your instance (defaults: postgres@localhost:5432).

psql postgres <<'SQL'
CREATE DATABASE singularity;
CREATE DATABASE workgraph;
CREATE DATABASE audit_governance;
CREATE DATABASE singularity_iam;
\c singularity
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS pgcrypto;
\c audit_governance
CREATE EXTENSION IF NOT EXISTS pgcrypto;
SQL

# Apply audit-gov schema (7 tables + rate_card seed)
psql -d audit_governance -f audit-governance-service/db/init.sql

2. Single env file

cat > .env.local <<'EOF'
export PG_HOST=localhost
export PG_PORT=5432
export PG_USER=postgres
export PG_PASS=postgres

export DATABASE_URL_AGENT_TOOLS="postgresql://${PG_USER}:${PG_PASS}@${PG_HOST}:${PG_PORT}/singularity"
export DATABASE_URL_WORKGRAPH="postgresql://${PG_USER}:${PG_PASS}@${PG_HOST}:${PG_PORT}/workgraph"
export DATABASE_URL_AUDIT_GOV="postgresql://${PG_USER}:${PG_PASS}@${PG_HOST}:${PG_PORT}/audit_governance"

# Shared JWT secret (32+ chars — workgraph-api enforces)
export JWT_SECRET="dev-secret-change-in-prod-min-32-chars!!"

# IAM points at pseudo (skip real IAM)
export AUTH_PROVIDER="iam"
export IAM_BASE_URL="http://localhost:8101/api/v1"

# Cross-service URLs
export AUDIT_GOV_URL="http://localhost:8500"
export PROMPT_COMPOSER_URL="http://localhost:3004"
export AGENT_RUNTIME_URL="http://localhost:3003"
export TOOL_SERVICE_URL="http://localhost:3002"
export AGENT_SERVICE_URL="http://localhost:3001"
export CONTEXT_FABRIC_URL="http://localhost:8000"
export MCP_SERVER_URL="http://localhost:7100"
export MCP_BEARER_TOKEN="demo-bearer-token-must-be-min-16-chars"

# LLM mock by default — no API keys required
export LLM_PROVIDER="mock"
export LLM_MODEL="mock-fast"
EOF

source .env.local

3. Install + push schemas (one-time)

( cd agent-and-tools          && npm install )
( cd workgraph-studio         && pnpm install )
( cd pseudo-iam-service       && npm install )
( cd audit-governance-service && npm install )
( cd mcp-server               && npm install )

# Prisma push + seed for agent-runtime
( cd agent-and-tools/apps/agent-runtime \
  && DATABASE_URL="$DATABASE_URL_AGENT_TOOLS" npx prisma db push \
  && DATABASE_URL="$DATABASE_URL_AGENT_TOOLS" npx prisma db seed )

# Prisma push for workgraph-api
( cd workgraph-studio/apps/api \
  && DATABASE_URL="$DATABASE_URL_WORKGRAPH" npx prisma db push )

# Python deps for context-api
pip install fastapi uvicorn httpx pydantic-settings python-jose[cryptography] \
            sqlalchemy aiosqlite

4. Boot — paste this script

Saves PIDs to .pids so a single kill cleans up.

mkdir -p logs && : > .pids
boot() {
  local name=$1; shift
  ( "$@" >> "logs/${name}.log" 2>&1 & echo $! >> .pids ) && \
    echo "$name (PID $!)  → tail -f logs/${name}.log"
}

# ---- Tier 0: zero-dep ----
boot pseudo-iam        bash -c "cd pseudo-iam-service        && JWT_SECRET=\"$JWT_SECRET\" PORT=8101 npm run dev"
boot audit-gov         bash -c "cd audit-governance-service  && DATABASE_URL=\"$DATABASE_URL_AUDIT_GOV\" PORT=8500 npm run dev"

sleep 2

# ---- Tier 1: agent-and-tools backend ----
boot agent-service     bash -c "cd agent-and-tools/apps/agent-service    && PORT=3001 AUDIT_GOV_URL=\"$AUDIT_GOV_URL\" JWT_SECRET=\"$JWT_SECRET\" npm run dev"
boot tool-service      bash -c "cd agent-and-tools/apps/tool-service     && PORT=3002 DATABASE_URL=\"$DATABASE_URL_AGENT_TOOLS\" AUDIT_GOV_URL=\"$AUDIT_GOV_URL\" JWT_SECRET=\"$JWT_SECRET\" npm run dev"
boot agent-runtime     bash -c "cd agent-and-tools/apps/agent-runtime    && PORT=3003 DATABASE_URL=\"$DATABASE_URL_AGENT_TOOLS\" AUDIT_GOV_URL=\"$AUDIT_GOV_URL\" JWT_SECRET=\"$JWT_SECRET\" npm run dev"
boot prompt-composer   bash -c "cd agent-and-tools/apps/prompt-composer  && PORT=3004 DATABASE_URL=\"$DATABASE_URL_AGENT_TOOLS\" AUDIT_GOV_URL=\"$AUDIT_GOV_URL\" JWT_SECRET=\"$JWT_SECRET\" npm run dev"

# ---- Tier 2: execution + orchestrator ----
boot mcp-server        bash -c "cd mcp-server                            && PORT=7100 MCP_BEARER_TOKEN=\"$MCP_BEARER_TOKEN\" LLM_PROVIDER=mock LLM_MODEL=mock-fast AUDIT_GOV_URL=\"$AUDIT_GOV_URL\" npm run dev"
boot context-api       bash -c "cd context-fabric/services/context_api_service && DATABASE_URL=\"$DATABASE_URL_AUDIT_GOV\" PORT=8000 AUDIT_GOV_URL=\"$AUDIT_GOV_URL\" MCP_SERVER_URL=\"$MCP_SERVER_URL\" MCP_BEARER_TOKEN=\"$MCP_BEARER_TOKEN\" uvicorn app.main:app --host 0.0.0.0 --port 8000"

sleep 3

# ---- Tier 3: workgraph ----
boot workgraph-api     bash -c "cd workgraph-studio/apps/api && PORT=8080 DATABASE_URL=\"$DATABASE_URL_WORKGRAPH\" JWT_SECRET=\"$JWT_SECRET\" AUTH_PROVIDER=iam IAM_BASE_URL=\"$IAM_BASE_URL\" AGENT_RUNTIME_URL=\"$AGENT_RUNTIME_URL\" TOOL_SERVICE_URL=\"$TOOL_SERVICE_URL\" AGENT_SERVICE_URL=\"$AGENT_SERVICE_URL\" PROMPT_COMPOSER_URL=\"$PROMPT_COMPOSER_URL\" CONTEXT_FABRIC_URL=\"$CONTEXT_FABRIC_URL\" AUDIT_GOV_URL=\"$AUDIT_GOV_URL\" npm run dev"

# ---- Tier 4: SPAs ----
boot agent-web         bash -c "cd agent-and-tools/web                   && AUDIT_GOV_URL=\"$AUDIT_GOV_URL\" AGENT_RUNTIME_URL=\"$AGENT_RUNTIME_URL\" TOOL_SERVICE_URL=\"$TOOL_SERVICE_URL\" AGENT_SERVICE_URL=\"$AGENT_SERVICE_URL\" PROMPT_COMPOSER_URL=\"$PROMPT_COMPOSER_URL\" npm run dev"
boot workgraph-web     bash -c "cd workgraph-studio/apps/web             && VITE_API_BASE=http://localhost:8080 VITE_PSEUDO_IAM_URL=http://localhost:8101/api/v1 VITE_AUTO_LOGIN=1 npm run dev"

echo
echo "All booted. Tail any service with: tail -f logs/<name>.log"
echo "Stop everything:                    kill \$(cat .pids) && rm .pids"

5. Smoke check

for url in \
  http://localhost:8101/health \
  http://localhost:8500/health \
  http://localhost:7100/health \
  http://localhost:8000/health \
  http://localhost:8080/health \
  "http://localhost:3000/api/runtime/agents/templates?scope=common&limit=3" \
  http://localhost:5174/ \
  ; do
  printf "%-65s %s\n" "$url" "$(curl -s -o /dev/null -w '%{http_code}' "$url")"
done

All seven should return 200. Open http://localhost:5174 for the demo.

Tear down

kill $(cat .pids) && rm .pids
# Optional — wipe data:
psql postgres -c "DROP DATABASE singularity; DROP DATABASE workgraph; DROP DATABASE audit_governance; DROP DATABASE singularity_iam;"

What's intentionally skipped

Skipped Impact
real IAM (iam-service) None for demo — pseudo-IAM signs JWTs workgraph accepts because they share JWT_SECRET
llm-gateway, context-memory, metrics-ledger None — context-api calls mcp-server directly; mcp-server's embedded LLM is mock
MinIO File uploads return 5xx; insights, Agent Studio, audit, cost all still work
portal (:5180), user-and-capability (:5175) Optional UI wrappers; :5174 + :3000 cover the demo path

Common boot failures: (a) wrong PG_USER/PG_PASS, (b) pgvector extension not installed in singularity, (c) port collision (lsof -i :3003).


Recent (M9.z – M11)

The platform layer (M11) and supporting milestones landed as a cohesive set; everything below is shipped + smoke-tested end-to-end.

Milestone What it added Verification
M9.z Approval pause/resume MCP /mcp/resume + cf /execute/resume + workgraph AgentRunStatus.PAUSED + POST /agent-runs/:id/approve. Single-use continuation tokens, 24h TTL. Full workflow → tool requires_approval → workgraph PAUSED → UI approve → MCP resumes loop → AWAITING_REVIEW
M10 Federated lookups workgraph /api/lookup/* proxies to IAM + agent-and-tools with user-JWT forwarding; NodeInspector pickers; Agent/Tool snapshot at AGENT_TASK start (externalTemplateId) Picker dropdowns populated from real services; snapshot row written on first run, reused afterwards
M11.a Service + Contract Registry New platform-registry :8090 + Postgres :5435; per-service self-register helper (TS + Python); 11 services + 47 capabilities + contracts browsable GET :8090/api/v1/services returns 12 (11 production + sample)
M11.b Reference Resolver GET /api/lookup/:entity/:id for 9 kinds; POST /api/lookup/resolve batch (200/207); write-time validation in workflow design POST/PATCH (422 on bad ref) bogus refs → 422 with field-level failure; valid → 201
M11.c Snapshot provenance sourceHash/sourceVersion/fetchedBy on snapshots; new prompt_profile_snapshots + capability_snapshots tables; canonical-JSON sha256 dedupe 3 runs of same workflow → 1 capability snapshot row
M11.d Unified Receipt envelope cf GET /receipts?trace_id= + workgraph GET /api/receipts?trace_id= joins workgraph + cf + MCP audit 14 receipts merged from 3 services in chronological order
M11.e Event Bus event_outbox + event_subscriptions + event_deliveries in 5 publishers (IAM Python, workgraph TS, agent-runtime TS, tool-service TS, agent-service TS); Postgres LISTEN/NOTIFY dispatcher with 30s safety sweep, HMAC, 5-attempt retry; workgraph receiver at POST /api/events/incoming; canonical envelope shape across all publishers Subscribe to *.created → trigger from any service → workgraph event_log captures with incoming.<event_name>
M11 follow-up OTel + Jaeger Auto-instrumentation in workgraph-api (TS), context-api (Python), tool-service (TS), agent-runtime (TS), agent-service (TS); Jaeger all-in-one in platform-registry compose; W3C traceparent propagated automatically Single trace 1cc8ef8ac9a1207b had 59 spans across 4 services. UI: http://localhost:16686
M11 follow-up Service-token auto-mint IAM POST /api/v1/auth/service-token + workgraph + cf bootstrap + IAM_BOOTSTRAP_USERNAME/PASSWORD env. Replaces 60-min admin-JWT-passing-via-env. Both services start with IAM_SERVICE_TOKEN="", mint 30-day tokens on first call
M11 follow-up Embedded LLM gateway in MCP LLM-agnostic provider router inside mcp-server: OpenAI Chat Completions, Anthropic Messages API (with tool-calling translation), GitHub Copilot Headless. Provider keys live in operator's local env. New GET /llm/providers route surfaces what's configured. All 4 providers route correctly; missing keys produce clean errors. Set OPENAI_API_KEY / ANTHROPIC_API_KEY / COPILOT_TOKEN in env to use real providers.

                     Singularity Portal (:5180)
                              │
            ┌─────────────────┼─────────────────┐
            ▼                 ▼                 ▼
     UserAndCapabillity  Agent & Tools     Workgraph Studio
            (:5175)        (:3000)            (:5174)
            │                 │                 │
            ▼                 ▼                 ▼
       IAM Service      ┌─prompt-composer (:3004)─┐
        (:8100)         │  agent-runtime  (:3003)  │      Workgraph API
            │           │  tool-service   (:3002)  │         (:8080)
            │           │  agent-service  (:3001)  │             │
            │           └──────────────────────────┘             │
            │                       │                            │
            │                       ▼                            │
            │             Context Fabric (:8000)                 │
            │             llm-gateway (:8001)                    │
            │             memory      (:8002)                    │
            │             metrics     (:8003)                    │
            │                                                    │
            └────────────── shared IAM JWT ──────────────────────┘

Table of Contents


What's in the box

App Role Stack Ports
singularity-iam-service Identity, orgs, teams, roles, capabilities, skills, JWT, MCP server registry, service-token mint, event bus Python · FastAPI · Postgres 8100, postgres 5433
agent-and-tools Agent definitions, tool registry, prompt assembly, agent CRUD UI; per-service event bus + OTel TypeScript monorepo · Express · Next.js · Prisma · Postgres+pgvector 3000–3004, postgres 5432
context-fabric LLM cost optimizer (context compaction + token-saving ledger), /execute orchestrator, /receipts join, OTel Python · 4× FastAPI · SQLite 8000–8003
mcp-server Per-tenant MCP execution engine + WS bridge + embedded LLM gateway (OpenAI / Anthropic / Copilot Headless / mock). Customer-deployed, holds its own provider keys. TypeScript · Express · WebSocket 7100
workgraph-studio Visual DAG designer + workflow runtime, federated /api/lookup/*, snapshot layer, unified /api/receipts, event bus + receiver, OTel React + ReactFlow + Zustand · Express + Prisma · MinIO 5174 (web) / 8080 (api), postgres 5434, minio 9000-9001
platform-registry Service + Contract Registry: every service self-registers on startup with capabilities + OpenAPI/event/node contracts TypeScript · Express · Postgres 8090, postgres 5435
UserAndCapabillity Visual admin SPA for IAM React 19 · Vite · Tailwind · Radix · Zustand 5175
singularity-portal The wrapper SPA — single login + dashboard tiles + deep links React 19 · Vite · Tailwind · Radix 5180
jaeger (observability) All-in-one OTel trace UI; receives spans from all instrumented services docker image 16686 (UI), 4317/4318 (OTLP)

11 production services. Each owns its database. capability_id is the join key across them; joins happen at the application layer, never in SQL.


The five planes

A useful mental model when deciding "which app should this feature live in?" — match the responsibility to its plane.

┌───────────────────────────────────────────────────────────┐
│ CONTROL          IAM + UserAndCapabillity + Portal        │
│  decides WHO can do WHAT; humans configure here           │
└───────────────────────────────────────────────────────────┘
                            │
┌───────────────────────────▼───────────────────────────────┐
│ ORCHESTRATION    workgraph-studio                         │
│  decides WHEN and IN WHAT ORDER work happens              │
└───────────────────────────────────────────────────────────┘
                            │
┌───────────────────────────▼───────────────────────────────┐
│ COMPOSITION      prompt-composer (inside agent-and-tools) │
│  decides WHAT TO SAY to the LLM (layered prompts +        │
│  workflow context + tool contracts + artifacts)           │
└───────────────────────────────────────────────────────────┘
                            │
┌───────────────────────────▼───────────────────────────────┐
│ DATA             agent-and-tools (registries, memory)     │
│  the WHAT of the work (templates, tools, knowledge)       │
└───────────────────────────────────────────────────────────┘
                            │
┌───────────────────────────▼───────────────────────────────┐
│ OPTIMIZATION     context-fabric                           │
│  makes LLM calls cheaper (compaction + cost tracking)     │
└───────────────────────────────────────────────────────────┘

Quick start

Prerequisites

  • Docker Desktop (or Docker Engine + the Compose v2 plugin)
  • 8 GB+ RAM available to Docker
  • Free host ports: 3000–3004, 5174–5175, 5180, 5432–5434, 8000–8003, 8080, 8100, 9000–9001
  • Optional: OPENROUTER_API_KEY in context-fabric/.env for real LLM calls (otherwise uses mock provider)

Option A — master compose (one shot)

cd /path/to/SingularityNeoNew
docker compose up -d                       # builds + starts 18 containers
docker compose ps                          # see what's running

Then open http://localhost:5180 and log in with admin@singularity.local / Admin1234!.

To tear down (keeps data):

docker compose down

To wipe data volumes:

docker compose down -v

Option B — singularity.sh CLI

Wraps the master compose with friendlier subcommands. Useful for starting/stopping individual services.

./singularity.sh up                        # start all
./singularity.sh up portal                 # start just the portal
./singularity.sh status                    # ps
./singularity.sh urls                      # color-coded URL cheatsheet
./singularity.sh logs workgraph-api -f     # follow logs
./singularity.sh restart prompt-composer
./singularity.sh stop workgraph-web
./singularity.sh down                      # stop all (keep data)
./singularity.sh nuke                      # stop + delete data volumes (confirms)
./singularity.sh login                     # smoke-test IAM /auth/local/login
./singularity.sh ls                        # list known service names
./singularity.sh build [service]           # rebuild image(s)
./singularity.sh help                      # full usage

Option C — per-app compose files

Each app still has its own docker-compose.yml if you want to run a subset without the master:

cd singularity-iam-service        && docker compose up -d
cd context-fabric                 && docker compose up -d
cd agent-and-tools                && docker compose up -d
cd workgraph-studio/infra/docker  && docker compose up -d   # postgres+minio+api+web
cd UserAndCapabillity             && npm install && npm run dev
cd singularity-portal             && npm install && npm run dev

Heads up: the per-app compose files use different container names + ports than the master (e.g. agentandtools-postgres vs. singularity-at-postgres). Don't mix them — pick one approach and stick with it.


Service inventory

Service URL Auth Notes
portal http://localhost:5180 IAM JWT the wrapper SPA — start here
user-and-capability http://localhost:5175 IAM JWT IAM admin SPA
workgraph-web http://localhost:5174 workgraph token Designer + Runtime UI
agent-web http://localhost:3000 optional JWT Next.js admin
iam-service http://localhost:8100/api/v1 bearer (login) OpenAPI: /docs
workgraph-api http://localhost:8080/api workgraph token DAG runtime
prompt-composer http://localhost:3004/api/v1 optional JWT /compose-and-respond
agent-runtime http://localhost:3003/api/v1 optional JWT agent templates, memory
tool-service http://localhost:3002/api/v1 optional JWT tool registry, /tools/discover, /tools/invoke
agent-service http://localhost:3001/api/v1 optional JWT agent CRUD
context-api http://localhost:8000 none /chat/respond, /docs (OpenAPI)
llm-gateway http://localhost:8001 none /llm/respond, /llm/models, /docs
context-memory http://localhost:8002 none /memory/messages, /context/compile
metrics-ledger http://localhost:8003 none /metrics/dashboard
iam-postgres localhost:5433 singularity / singularity singularity_iam DB
at-postgres localhost:5432 postgres / singularity singularity DB (pgvector)
wg-postgres localhost:5434 workgraph / workgraph_secret workgraph DB
wg-minio http://localhost:9000 (console :9001) workgraph / workgraph_secret artifact storage

Using the platform

Sign in

Open http://localhost:5180 → Sign in with admin@singularity.local / Admin1234!. The portal stores the IAM JWT in localStorage under singularity-portal.auth and forwards it to every backend.

What you'll see

The portal home shows four live tiles pulling from each backend:

  • My open tasks — counts + recent items from workgraph /api/runtime/inbox
  • Workflow runs — active and recent WorkflowInstances across templates
  • LLM cost & token savings — total tokens saved + cost saved from metrics-ledger
  • Your capabilities — IAM capability list filtered to the signed-in user

The sidebar's Apps section deep-links to:

  • Workgraph Designer — the visual DAG editor + runtime UI
  • Agent & Tools — agents, prompts, tools admin
  • IAM Admin — users, roles, capabilities

The deep-link apps each handle their own auth right now (workgraph-web has its own login). Single-sign-on across all UIs is on the roadmap (set AUTH_PROVIDER=iam on workgraph + change tile auth in user-and-capability).


End-to-end demo

This is the wire that ships in the current build: a workflow runs, calls the composer, calls context-fabric, gets a response, persists three correlated audit records.

# 1. Bring up everything
./singularity.sh up
sleep 30   # let migrations + first builds complete

# 2. Login to workgraph-api (its own user, separate from IAM today)
TOKEN=$(curl -sS -X POST http://localhost:8080/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"admin@workgraph.local","password":"admin123"}' | jq -r .token)

# 3. Create a workgraph Agent (workgraph's own registry)
AGENT_ID=$(curl -sS -X POST http://localhost:8080/api/agents \
  -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -d '{"name":"Demo Agent","provider":"mock","model":"mock-fast","skillIds":[]}' | jq -r .id)

# 4. Find a seeded composer agentTemplateId (from agent-and-tools/agent-runtime seed)
TEMPLATE_ID=$(docker exec singularity-at-postgres psql -U postgres -d singularity \
  -tAc 'SELECT id FROM "AgentTemplate" LIMIT 1;' | tr -d ' ')

# 5. Build a 2-node DAG: START → AGENT_TASK → END
WF_ID=$(curl -sS -X POST http://localhost:8080/api/workflow-templates \
  -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -d '{"name":"E2E Demo"}' | jq -r .id)

START=$(curl -sS -X POST http://localhost:8080/api/workflow-templates/$WF_ID/design/nodes \
  -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -d '{"nodeType":"START","label":"start","positionX":0,"positionY":0}' | jq -r .id)

AGENT_NODE=$(curl -sS -X POST http://localhost:8080/api/workflow-templates/$WF_ID/design/nodes \
  -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -d "{
    \"nodeType\":\"AGENT_TASK\",\"label\":\"audit\",\"positionX\":200,\"positionY\":0,
    \"config\":{
      \"agentId\":\"$AGENT_ID\",
      \"agentTemplateId\":\"$TEMPLATE_ID\",
      \"task\":\"Audit {{instance.vars.module}} for OWASP issues.\",
      \"modelOverrides\":{\"provider\":\"mock\",\"model\":\"mock-fast\"},
      \"contextPolicy\":{\"optimizationMode\":\"medium\"},
      \"toolDiscovery\":{\"enabled\":false}
    }
  }" | jq -r .id)

END=$(curl -sS -X POST http://localhost:8080/api/workflow-templates/$WF_ID/design/nodes \
  -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -d '{"nodeType":"END","label":"end","positionX":400,"positionY":0}' | jq -r .id)

curl -sS -X POST http://localhost:8080/api/workflow-templates/$WF_ID/design/edges \
  -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -d "{\"sourceNodeId\":\"$START\",\"targetNodeId\":\"$AGENT_NODE\",\"edgeType\":\"SEQUENTIAL\"}" > /dev/null
curl -sS -X POST http://localhost:8080/api/workflow-templates/$WF_ID/design/edges \
  -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -d "{\"sourceNodeId\":\"$AGENT_NODE\",\"targetNodeId\":\"$END\",\"edgeType\":\"SEQUENTIAL\"}" > /dev/null

# 6. Run with a Mustache var
RUN_ID=$(curl -sS -X POST http://localhost:8080/api/workflow-templates/$WF_ID/runs \
  -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -d '{"name":"demo run","vars":{"module":"auth-service"},"globals":{}}' | jq -r .id)

curl -sS -X POST http://localhost:8080/api/workflow-instances/$RUN_ID/start \
  -H "Authorization: Bearer $TOKEN" > /dev/null

sleep 5

# 7. Inspect the result — three correlated IDs
docker exec singularity-wg-postgres psql -U workgraph -d workgraph -tAc "
SELECT 'promptAssemblyId: ' || (\"structuredPayload\"::json->>'promptAssemblyId') ||
       E'\nmodelCallId:      ' || (\"structuredPayload\"::json->>'modelCallId') ||
       E'\ntokens_saved:     ' || (\"structuredPayload\"::json->'optimization'->>'tokens_saved')
FROM agent_run_outputs
WHERE \"runId\" IN (SELECT id FROM agent_runs WHERE \"instanceId\" = '$RUN_ID');"

# Inspect the assembled prompt in composer
ASSEMBLY_ID=$(docker exec singularity-wg-postgres psql -U workgraph -d workgraph -tAc \
  "SELECT \"structuredPayload\"::json->>'promptAssemblyId' FROM agent_run_outputs \
   WHERE \"runId\" IN (SELECT id FROM agent_runs WHERE \"instanceId\" = '$RUN_ID');" | tr -d ' ')
docker exec singularity-at-postgres psql -U postgres -d singularity -tAc \
  "SELECT \"contentSnapshot\" FROM \"PromptAssemblyLayer\" \
   WHERE \"promptAssemblyId\" = '$ASSEMBLY_ID' AND \"layerType\" = 'TASK_CONTEXT';"
# → "# Current Task\nAudit auth-service for OWASP issues."   ✓ var substituted

singularity.sh cheatsheet

up [service]        start all (or just one)
down                stop everything (keeps volumes)
nuke                stop + delete all data volumes (confirms)
stop <service>      stop one service
restart <service>   restart one service
status (ps)         list services + state
logs <service>      tail logs (pass -f to follow)
build [service]     rebuild image(s)
urls                printable URL cheatsheet
ls                  list known service names
login               smoke-test IAM /auth/local/login
help                usage

Architecture deep-dive

The composition plane

prompt-composer (port 3004, in the agent-and-tools workspace) is the new center of gravity. It owns the prompt-assembly tables (PromptProfile, PromptLayer, PromptAssembly) and exposes:

  • POST /api/v1/prompt-profiles (CRUD)
  • POST /api/v1/prompt-layers (CRUD)
  • POST /api/v1/prompt-assemblies (legacy assemble — same contract as agent-runtime used to expose)
  • POST /api/v1/compose-and-respond (the new value-add)

/compose-and-respond is what workgraph's AgentTaskExecutor calls. It:

  1. Builds a substitution context from workflowContext ({{instance.vars.x}}, {{node.priorOutputs.y}}, {{capability.metadata.z}}, {{artifacts.<label>.excerpt}}, {{task}})
  2. Loads the template's base profile + binding overlay layers
  3. Adds capability context, knowledge artifacts, distilled memory layers
  4. Adds workflow-phase layers
  5. Builds a TOOL_CONTRACT layer from static grants + tool-service /tools/discover, with both JSON Schema and natural-language summary per tool
  6. Renders artifacts as ARTIFACT_CONTEXT layers (priority 600) — supports inline content, pre-extracted excerpt, or minioRef (placeholder; full fetch is M4.1 work)
  7. Adds TASK_CONTEXT (priority 900)
  8. Appends node-level EXECUTION_OVERRIDE layers (priority 9999)
  9. Sorts by priority, concatenates, hashes, persists PromptAssembly + PromptAssemblyLayer rows
  10. Calls context-fabric /chat/respond with the assembled system_prompt and task as message
  11. Returns a unified response with three correlation IDs: promptAssemblyId, modelCallId, contextPackageId

Workgraph wire (M5)

apps/api/src/modules/workflow/runtime/executors/AgentTaskExecutor.ts does the M5 plumbing:

  • Reads node.config for agentTemplateId, task, optional artifacts/overrides/modelOverrides/contextPolicy
  • Reads instance.context._vars and instance.context._globals
  • Walks prior AgentRun outputs to populate priorOutputs
  • POSTs to prompt-composer /compose-and-respond
  • Persists the response on AgentRunOutput.structuredPayload with full correlation
  • Sets AgentRun.status = AWAITING_REVIEW on success, FAILED on composer error
  • Emits AgentRunStarted/AgentRunCompleted/AgentRunFailed outbox events

Mental-model file

The full architecture history (5+ rounds of decisions, gap analysis, target topology, phased rollout) lives at ~/.claude/plans/in-the-singularityneonew-i-agile-fern.md.


Migration history

The composition plane was added in five milestones, each verified end-to-end:

Milestone What it delivered
M1 Scaffolded apps/prompt-composer + relocated PromptProfile/PromptLayer/PromptAssembly/PromptAssemblyLayer Prisma models
M2 Mirrored prompt CRUD endpoints at parity (profiles, layers, attach, assemblies). Smoke-tested via curl against shared Postgres.
M3 Cut over the agent-and-tools admin (web/) to call composer instead of agent-runtime: new /api/composer/* Next.js rewrite, web/Dockerfile gained PROMPT_COMPOSER_URL build-arg (Next.js bakes rewrites at build time, not runtime), agent-runtime's prompt route mount removed
M4 Added POST /compose-and-respond — workflow context, artifacts, Mustache substitution, tool-service discovery, context-fabric call, three correlation IDs
M5 Wired workgraph's AgentTaskExecutor to call /compose-and-respond. Fixed AgentRun.startedAt/completedAt lifecycle. Fixed workgraph Dockerfile (workspace context + Alpine OpenSSL) so it actually builds and runs in production.

Troubleshooting

Two Postgres servers on :5432

If you have a Homebrew Postgres running on localhost:5432 it'll shadow Docker's at-postgres. Symptom: clients connecting to localhost:5432 from the host get the Homebrew DB (no Singularity tables) while clients inside the Docker network see the right one. Fix:

brew services stop postgresql@14    # or whatever version

(Or remap the host port for at-postgres in the master compose to e.g. 5435:5432.)

Port 5433 is taken (workgraph postgres can't start)

The IAM Postgres binds 5433. The master compose remaps workgraph postgres to 5434 to avoid this. If you also run the original workgraph-studio/infra/docker/docker-compose.yml, edit its host port from 5433:5432 to 5434:5432.

Workgraph rejects the IAM JWT (you see 401s on the My-Tasks tile)

Workgraph runs AUTH_PROVIDER=local by default — it has its own user table and login endpoint. The portal sends the IAM JWT to all backends; workgraph 401s on it. The portal handles this gracefully (only the IAM client is "authoritative" for session — workgraph 401s show as a tile error, the user stays signed in).

To make workgraph honor IAM JWTs, set on workgraph-api:

environment:
  AUTH_PROVIDER: iam
  IAM_BASE_URL: http://iam-service:8100/api/v1
  IAM_SERVICE_TOKEN: <a long-lived token from IAM>

Workgraph already ships an iam/client.ts that handles this — just hasn't been turned on.

Prisma "OpenSSL not detected" inside Alpine

Some images are missing libssl. Fix in the affected Dockerfile:

RUN apk add --no-cache openssl

Vite dev says "port 5180 in use"

The master compose runs the portal in nginx on :5180. If you also npm run dev it locally, kill one or the other.

lsof -i :5180          # find the PID
kill <pid>

Master compose service didn't pick up an env change

Compose only re-reads env on container creation. Use:

./singularity.sh restart <service>   # picks up env from docker-compose.yml
docker compose up -d <service>       # same

For portal/web env changes (Vite bakes some at build time), rebuild:

./singularity.sh build <service>
./singularity.sh restart <service>

Tearing everything down

./singularity.sh down                    # stop, keep data
./singularity.sh nuke                    # stop + delete all volumes (ASKS first)
docker compose down -v --remove-orphans  # raw equivalent

Open items

These are real gaps, not nice-to-haves:

  • SSO — only IAM is authoritative for the portal. Workgraph-api needs AUTH_PROVIDER=iam flipped on so the same IAM JWT works platform-wide.
  • Workgraph Agent table vs. agent-and-tools AgentTemplate — two parallel registries today. The "mirror / federate / collapse" decision is unresolved.
  • AgentRun correlation columnspromptAssemblyId, modelCallId, contextPackageId are stuffed in structuredPayload JSON. Promote to dedicated columns for queryability.
  • Streaming — composer + workgraph still synchronous on /chat/respond. No SSE token streaming yet.
  • MCP bridge — slated to live inside context-fabric on :8004; not built. Needs a per-capability MCP server registry in IAM (mcp_servers, mcp_server_secrets, mcp_capability_bindings).
  • Knowledge / learning pipelineCapabilityCodeSymbol/CapabilityCodeEmbedding/CapabilityKnowledgeArtifact schemas exist in agent-runtime; no symbol extractor or embedder service populates them. The learning_candidates → learning_profiles → DistilledMemory pipeline lacks a distillation worker + promotion UI.
  • Per-tenant multi-tenancy — capability scoping is the soft boundary; no schema-per-tenant or hard isolation yet.
  • Observability — no OpenTelemetry/Jaeger across the stack; correlation IDs exist but aren't wired into traces.
  • Production deploy — the Dockerfiles work; CI/CD configs (.github/workflows) do not exist yet.

Branding

The Singularity wordmark and the silver swirl mark are shared assets used by every UI in the platform. They live in one place:

branding/
├── README.md                      brand guide: colors, typography, placement rules
├── tokens.css                     CSS custom properties (--brand-forest, --brand-green, …)
├── singularity-logo.png           full lockup (drop the official PNG here)
└── singularity-mark.png           swirl-only — favicons + tight headers (optional)

Drop the brand asset once, propagate everywhere

# 1. Save the official logo PNG at:
#       branding/singularity-logo.png      (full lockup)
#       branding/singularity-mark.png      (swirl-only — optional; falls back to logo)

# 2. Sync to every app's public/ directory:
./bin/sync-branding.sh

# Output:
#   ✓ synced → singularity-portal/public/
#   ✓ synced → UserAndCapabillity/public/
#   ✓ synced → agent-and-tools/web/public/
#   ✓ synced → workgraph-studio/apps/web/public/
#   ✓ 4 app(s) updated.

Re-run any time the canonical files change. Each app loads /singularity-mark.png (with /singularity-logo.png as a graceful fallback) from its own public/ root, so cross-domain CORS is never a concern.

Visual identity at a glance

Element Value
Sidebar background forest gradient #0E3B2D → #082821
Wordmark "Singularity" — Inter 700, tracking +0.04em, warm white #F5F2EA
Tagline "GOVERNED AGENTIC DELIVERY" — Inter 600, tracking +0.18em, ~55% opacity
Primary action green #00843D (hover #006236)
Active nav green left/right border #00A651 + 8% white background
Tab title every app: <title>Singularity — <App Name></title>
Favicon /favicon.png (synced from branding/singularity-mark.png)

See branding/README.md for the full guide (typography rules, placement, prohibited usage).


Repo layout

SingularityNeoNew/
├── docker-compose.yml          # master compose (18 services)
├── singularity.sh              # CLI wrapper
├── README.md                   # this file
├── branding/                   # canonical logo + tokens.css (one drop, all apps consume)
├── bin/
│   └── sync-branding.sh        # copies branding/*.png into each app's public/
│
├── singularity-iam-service/    # Python FastAPI — identity
├── agent-and-tools/            # TS monorepo — agents/tools/composer/runtime
│   ├── apps/
│   │   ├── agent-service/
│   │   ├── tool-service/
│   │   ├── agent-runtime/
│   │   └── prompt-composer/    # NEW (composition plane)
│   ├── packages/
│   ├── web/                    # Next.js admin
│   └── docker-compose.yml      # per-app compose (alternative to master)
├── context-fabric/             # Python — 4× FastAPI for LLM optimization
├── workgraph-studio/           # TS pnpm workspace — DAG designer + runtime
│   ├── apps/api/               # Express + Prisma
│   ├── apps/web/               # React + ReactFlow
│   ├── packages/{shared-types,engine}/
│   └── infra/docker/docker-compose.yml
├── UserAndCapabillity/         # React + Vite — IAM admin SPA
└── singularity-portal/         # NEW — wrapper SPA (this is the front door)

License & ownership

Internal Singularity Neo platform. See per-app READMEs for component-level details:

About

Singularity — multi-service AI agent platform: IAM, agent-and-tools, context-fabric, workgraph-studio, mcp-server, platform-registry. Federated lookups, snapshot+receipt+event-bus layers, OpenTelemetry, embedded LLM gateway.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors