If you are making a plan, save it in the temp-plans/ folder. If not making a plan, implement directly.
Adding a New Service
Quick Start
bash
mkdir {servicename}
cd {servicename}
go mod init {servicename}
⚠️ IMPORTANT: Add {servicename}/{servicename} to .gitignore immediately (the built binary should never be committed)
Non-Obvious Decisions
1. Dockerfiles build from project root
Because services import shared api/generated/ code:
dockerfile
# {servicename}/Dockerfile
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
COPY api/ api/
COPY {servicename}/ {servicename}/
WORKDIR /app/{servicename}
RUN go mod download && CGO_ENABLED=0 go build -o /bin/{servicename} .
FROM alpine:latest
COPY --from=builder /bin/{servicename} /bin/{servicename}
CMD ["/bin/{servicename}"]
2. go.mod needs replace directive
go
require accelerator v0.0.0 replace accelerator => ../
And add to go.work:
go
use ./{servicename}
3. docker-compose uses Taskfile vars
yaml
{servicename}:
build:
context: . # Project root, not service folder
dockerfile: {servicename}/Dockerfile
environment:
SAMPLE_RATE: ${SAMPLE_RATE} # From Taskfile vars
CHANNELS: ${CHANNELS}
NATS_URL: nats://nats:4222 # Docker-specific, hardcoded
4. Use generated types, not manual ones
go
import accelerator "accelerator/api/generated"
// Channel paths from generated constants
accelerator.AudiorecorderAudioRawChannelPath
// Controllers handle pub/sub
ctrl, _ := accelerator.NewAppController(broker)
ctrl.SubscribeTo{Operation}(ctx, handler)
ctrl.SendAs{Operation}(ctx, msg)
5. Config has no subjects
Subjects come from generated constants. Config only has:
- •
NATSUrl(with localhost default for native dev) - •Service-specific settings
Native vs Docker Services
Ask: "Does this service need native OS access?"
| Answer | Action |
|---|---|
| No (only NATS pub/sub) | Add to docker-compose.yml, include in task up |
| Yes (mic, keyboard, etc.) | Create standalone task {servicename} to run natively |
Examples:
- •
bindingregistry→ Docker (only reads/writes NATS) - •
keylogger→ Native (needs macOS CGEventTap for keyboard capture) - •
audiorecorder→ Native (needs mic access)
Unit Tests
1. Create test files
Add tests in {servicename}/*_test.go. Mock external dependencies (NATS, keylogger events, etc.) by passing simulated data directly to functions.
2. Create test Dockerfile
Dockerfiles/{servicename}.test.Dockerfile:
dockerfile
FROM golang:1.22-alpine
WORKDIR /app
# Copy root module with generated code
COPY go.mod go.sum ./
COPY api/ api/
# Copy service module
COPY {servicename}/go.mod {servicename}/go.sum ./{servicename}/
WORKDIR /app/{servicename}
RUN go mod download
# Copy service source
WORKDIR /app
COPY {servicename}/ {servicename}/
WORKDIR /app/{servicename}
CMD ["go", "test", "-v", "./..."]
3. Add to docker-compose.test.yml
yaml
test-{servicename}:
build:
context: .
dockerfile: Dockerfiles/{servicename}.test.Dockerfile
4. Add to Taskfile.yml
In the test:docker task, add a run command (with other Go tests):
yaml
- SAMPLE_RATE={{.SAMPLE_RATE}} CHANNELS={{.CHANNELS}} SAVE_INTERVAL_SEC={{.SAVE_INTERVAL_SEC}} docker compose -f docker-compose.test.yml run --rm test-{servicename}
After Adding Service
- •⚠️ CRITICAL: Add
{servicename}/{servicename}to.gitignore- The compiled Go binary must be gitignored before any commits - •Update
api/asyncapi.yamlwith channels/messages - •Run
task generate - •Add Dockerfile to
Dockerfiles/{servicename}.Dockerfile - •Add to
docker-compose.ymlOR createtask {servicename}(see above)- •If Docker service: Also add to
task upcommand inTaskfile.yml
- •If Docker service: Also add to
- •Add unit tests and wire into
task test:docker(see Unit Tests section above) - •Update
.env.exampleif the service requires API keys or secrets (e.g.,GOOGLE_API_KEY,ANTHROPIC_API_KEY) - •Update
PIPELINE.md- Add the service to:- •Routing Table (Input/Output env vars)
- •Current Connections table (NATS subjects and payload types)
- •Payload Types section (document any new CloudEvent schemas)
- •ASCII Data Flow diagram - Add the new service to the visual pipeline diagram
Naming
| What | Pattern | Example |
|---|---|---|
| NATS subject | {component}.{entity}.{action} | transcriber.transcript.ready |
| CloudEvents type | com.accelerator.{component}.{event} | com.accelerator.transcriber.transcriptReady |