Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 13 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ just instance show # Print active instance number
just instance use 5 # Set active instance for this workspace
```

**Instance env files**:

```bash
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 values
```

**URLs**:

- Frontend: `http://localhost:${5173 + instance*100}`
Expand All @@ -43,14 +53,15 @@ Local development runs as **multiple app instances** (PM2) on top of **one share
- 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}.env` and can be managed with `just 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 N` then run `just dev` / `bun run test:e2e`, or
- explicit instance invocation (`just dev N ...`) for one-off commands.
- explicit env override (`SHIPSEC_INSTANCE=N just dev ...`) for one-off commands.

**Ports / URLs**

Expand All @@ -61,7 +72,7 @@ Local development runs as **multiple app instances** (PM2) on top of **one share
**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: `just dev N start`.
- When asked to run E2E, confirm the instance and ensure that instance is running: `SHIPSEC_INSTANCE=N just dev start` (or `just instance use N` then `just dev start`).

**Keep docs in sync**

Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,12 @@ Run multiple isolated dev instances on one machine for parallel feature work:
# Instance 0 (default)
just dev

# Instance 1 — offset ports (frontend :5273, backend :3311)
SHIPSEC_INSTANCE=1 just dev
# Switch active workspace instance
just instance use 1
just dev

# Manage per-instance env files
just instance-env init 1
```

Each instance gets its own frontend port, backend port, database, and Temporal namespace while sharing a single Docker infra stack. See [Multi-Instance Development Guide](docs/MULTI-INSTANCE-DEV.md) for full details.
Expand Down
66 changes: 50 additions & 16 deletions docs/MULTI-INSTANCE-DEV.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ ShipSec Studio supports running multiple isolated dev instances (0-9) on one mac
# Instance 0 (default) — works exactly as before
just dev

# Instance 1 — runs on offset ports (frontend :5273, backend :3311)
SHIPSEC_INSTANCE=1 just dev
# Check active instance for this workspace
just instance show

# Or persist the choice for this workspace
echo 1 > .shipsec-instance
just dev # now uses instance 1
# Persist instance 1 for this workspace
just instance use 1
just dev # now starts instance 1

# Stop your instance
SHIPSEC_INSTANCE=1 just dev stop
just dev stop
```

## How It Works
Expand All @@ -41,9 +41,36 @@ The instance is resolved in this order:
SHIPSEC_INSTANCE=2 just dev

# Per-workspace (persistent)
echo 2 > .shipsec-instance
just instance use 2
just dev
```

## Instance Env Files

PM2 can read per-instance env files from:

- `.instances/instance-N/backend.env`
- `.instances/instance-N/worker.env`
- `.instances/instance-N/frontend.env`

Use `just instance-env` to manage them:

```bash
# Create env files for an instance (uses app/.env, or falls back to app/.env.example)
just instance-env init 2

# Re-apply only instance-scoped values (ports, DB, Temporal namespace)
just instance-env update 2

# Copy env files between instances and rescope values
just instance-env copy 2 5

# Show file presence + computed values
just instance-env show 5
```

`just dev` auto-initializes env files for the active instance when missing (without overwriting existing files).

## Port Map

Ports are offset by `N * 100`:
Expand Down Expand Up @@ -82,23 +109,29 @@ The Vite dev server proxies `/api` calls to the correct backend port automatical

## Commands

All commands respect `SHIPSEC_INSTANCE`:
All commands respect `SHIPSEC_INSTANCE` (or the active instance selected by `just instance use N`):

```bash
# Select instance once per workspace
just instance use 1

# Start
SHIPSEC_INSTANCE=1 just dev
just dev

# Stop (only stops PM2 apps; infra stays running for other instances)
SHIPSEC_INSTANCE=1 just dev stop
just dev stop

# Logs (filtered to your instance's PM2 apps)
SHIPSEC_INSTANCE=1 just dev logs
just dev logs

# Status
SHIPSEC_INSTANCE=1 just dev status
just dev status

# Clean (stops PM2 apps; only tears down infra if instance 0)
SHIPSEC_INSTANCE=1 just dev clean
just dev clean

# One-off override without changing active workspace instance
SHIPSEC_INSTANCE=3 just dev
```

When stopping/cleaning instance 0, Docker infra is also torn down. For non-zero instances, only the PM2 apps are stopped (since other instances may still need the shared infra).
Expand Down Expand Up @@ -128,9 +161,10 @@ lsof -i :3311 # backend instance 1
### Instance is unhealthy but infra is fine

```bash
SHIPSEC_INSTANCE=1 just dev logs
SHIPSEC_INSTANCE=1 just dev stop
SHIPSEC_INSTANCE=1 just dev
just instance use 1
just dev logs
just dev stop
just dev
```

### Infra conflicts / stuck containers
Expand Down
54 changes: 54 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ dev action="start":
exit 1
fi

# Auto-init instance env files if missing (never overwrites)
if [ "$INST" != "0" ] || [ ! -d ".instances/instance-0" ]; then
./scripts/instance-env.sh init "$INST"
fi

if [ "$SECURE_MODE" = "true" ]; then
echo "🔐 Starting development environment (Clerk auth, instance ${INST})..."

Expand Down Expand Up @@ -541,6 +546,46 @@ build:
docker compose -f docker/docker-compose.full.yml build
echo "✅ Images built"

# Manage active multi-instance selection
instance action="show" value="":
#!/usr/bin/env bash
set -euo pipefail

case "{{action}}" in
show)
./scripts/active-instance.sh get
;;
use)
if [ -z "{{value}}" ]; then
echo "Usage: just instance use <0-9>"
exit 1
fi
./scripts/active-instance.sh set "{{value}}"
;;
*)
echo "Usage: just instance [show|use <0-9>]"
exit 1
;;
esac

# === Instance Environment ===

# Initialize instance env files (creates from .env or .env.example, never overwrites)
instance-init instance="":
#!/usr/bin/env bash
set -euo pipefail
INST="{{instance}}"
if [ -z "$INST" ]; then
INST="$(./scripts/active-instance.sh get)"
fi
./scripts/instance-env.sh init "$INST"

# Manage instance env files (init, update, copy, show)
instance-env +args:
#!/usr/bin/env bash
set -euo pipefail
./scripts/instance-env.sh {{args}}

# === Help ===

help:
Expand Down Expand Up @@ -576,6 +621,15 @@ help:
@echo " just infra logs View infrastructure logs"
@echo " just infra clean Remove infrastructure data"
@echo ""
@echo "Multi-Instance:"
@echo " just instance show Show active instance"
@echo " just instance use N Persist active instance in .shipsec-instance"
@echo " just instance-init [N] Init env files for instance N"
@echo " just instance-env init [N] [--force] Generate env files"
@echo " just instance-env update [N] Patch instance-specific vars"
@echo " just instance-env copy SRC DEST Copy env between instances"
@echo " just instance-env show [N] Show instance config"
@echo ""
@echo "Utilities:"
@echo " just status Show status of all services"
@echo " just db-reset Reset database"
Expand Down
Loading