Security workflow orchestration platform. Visual builder + Temporal for reliability.
frontend/— React + Vitebackend/— NestJS APIworker/— Temporal activities + componentspackages/— Shared code (component-sdk, backend-client)
just init # First time setup
just dev # Start the active instance (default: 0)
just dev stop # Stop the active instance (does NOT stop shared infra)
just dev stop all # Stop all instances + shared infra
just dev logs # View logs for the active instance
just help # All commandsActive instance:
just instance show # Print active instance number
just instance use 5 # Set active instance for this workspaceInstance env files:
just instance-init 5 # Initialize .instances/instance-5/*.env
just instance-env init 5 # Create from app/.env or app/.env.example
just instance-env update 5 # Re-apply instance-scoped vars
just instance-env copy 5 6 # Copy env setup from instance 5 -> 6
just instance-env show 6 # Show file status and computed valuesURLs:
- Frontend:
http://localhost:${5173 + instance*100} - Backend:
http://localhost:${3211 + instance*100} - Temporal UI (shared): http://localhost:8081
Full details: docs/MULTI-INSTANCE-DEV.md
Local development runs as multiple app instances (PM2) on top of one shared Docker infra stack.
- Shared infra (Docker Compose project
shipsec-infra): Postgres/Temporal/Redpanda/Redis/MinIO/Loki on fixed ports. - Per-instance apps:
shipsec-{frontend,backend,worker}-N. - Isolation is via per-instance DB + Temporal namespace/task queue + Kafka topic suffixing + instance-scoped Kafka consumer groups/client IDs (not per-instance infra containers).
- The workspace can have an active instance (stored in
.shipsec-instance, gitignored). - Instance env files are stored at
.instances/instance-N/{backend,worker,frontend}.envand can be managed withjust instance-env ....
Agent rule: before running any dev commands, ensure you’re targeting the intended instance.
- Always check:
just instance show - If the task is ambiguous (logs, curl, E2E, “run locally”, etc.), ask the user which instance to use.
- If the user says “use instance N”, prefer either:
just instance use Nthen runjust dev/bun run test:e2e, or- explicit env override (
SHIPSEC_INSTANCE=N just dev ...) for one-off commands.
Ports / URLs
- Frontend:
5173 + N*100 - Backend:
3211 + N*100 - Temporal UI (shared): http://localhost:8081
E2E tests
- E2E targets the backend for
SHIPSEC_INSTANCE(or the active instance). - When asked to run E2E, confirm the instance and ensure that instance is running:
SHIPSEC_INSTANCE=N just dev start(orjust instance use Nthenjust dev start).
Keep docs in sync
If you change instance/infra behavior (justfile/scripts/pm2 config), update docs/MULTI-INSTANCE-DEV.md and this section accordingly in the same PR.
bun --cwd backend run generate:openapi
bun --cwd packages/backend-client run generatebun run test # All tests
bun run typecheck # Type check
bun run lint # Lintjust db-reset # Reset database
bun --cwd backend run migration:push # Push schema
bun --cwd backend run db:studio # View data- TypeScript, 2-space indent
- Conventional commits with DCO:
git commit -s -m "feat: ..." - Tests alongside code in
__tests__/folders - E2E Tests: Mandatory for significant features. Place in
e2e-tests/folder. - GitHub CLI: Use
ghfor all GitHub operations (issues, PRs, actions, releases). Never use browser automation for GitHub tasks.
Before writing ANY frontend code that fetches data or adds a page, you MUST read these files first:
frontend/docs/state.md— Decision guide: TanStack Query vs Zustand, hook patterns, anti-patternsfrontend/docs/performance.md— Stale time tiers, bundle splitting, prefetch patterns, query key architecturefrontend/src/lib/queryKeys.ts— Existing query key factories (add new keys here, never inline)- Browse
frontend/src/hooks/queries/— Follow existing hook naming conventions (use<Domain>Queries.ts)
- All API data must use TanStack Query hooks in
frontend/src/hooks/queries/. Never useuseState+useEffectto fetch backend data — this is the single most important frontend rule. - Query keys go in
frontend/src/lib/queryKeys.ts(org-scoped, factory functions). - After mutations, invalidate the relevant query cache via
queryClient.invalidateQueries()— do not manually update local state. - Derive data from query results using
useMemo, not by copying into separateuseState. - Zustand stores are for client-only UI state (canvas, timeline, auth). Never store API data in Zustand.
See frontend/docs/state.md for patterns, anti-patterns, and the full decision guide.
See frontend/docs/performance.md for the complete reference with code examples.
- Every new page must use
React.lazy()inApp.tsx. Add the route toroutePrefetchMapinsrc/lib/prefetch-routes.ts. - Set
staleTime: Infinityfor static/reference data (components, templates, providers). The 30s default is wrong for them. - Use
skipTokenfor conditional queries instead ofenabled: falsealone. SeeuseRunQueries.ts. - Granular Zustand selectors:
useStore((s) => s.field), neverconst store = useStore(). - No N+1 queries: never call a query hook inside
.map(). Use a batched endpoint (seeuseMcpGroupsWithServers).
Full details: docs/architecture.mdx
Frontend ←→ Backend ←→ Temporal ←→ Worker
↓
Component Execution
↓
Terminal(Redis) | Events(Kafka) | Logs(Loki)
↓
Frontend (SSE/WebSocket)
- inline — TypeScript code (HTTP calls, transforms, file ops)
- docker — Containers (security tools: Subfinder, DNSX, Nuclei)
- remote — External executors (future: K8s, ECS)
- Terminal: Redis Streams → SSE → xterm.js
- Events: Kafka → WebSocket
- Logs: Loki + PostgreSQL
<skills_system priority="1">
When tasks match a skill, load it: `cat .claude/skills//SKILL.md`<available_skills> component-development Creating components (inline/docker). Dynamic ports, retry policies, PTY patterns, IsolatedContainerVolume. project performance-review Review code changes for frontend performance anti-patterns. Checks stale times, bundle splitting, Zustand selectors, N+1 queries, and React rendering. project stress-test-frontend Run a frontend load testing audit. Seeds data, tests all pages via Chrome DevTools MCP, records network calls, TanStack queries, DOM sizes, and generates a timestamped report. project </available_skills>
</skills_system>