| name | casting-a-new-service |
| description | Use when bootstrapping a brand-new microservice on github.com/tink3rlabs/magic — clones the service template, applies wizard answers, runs a smoke build |
casting-a-new-service
Produces a complete, building Go service from templates/service/ with placeholders replaced and wizard answers applied. The cast ends with go vet ./... && go build ./... passing in the target directory.
Prerequisites
- Target directory is empty, OR a freshly
git init-ed directory with no Go files.
- If neither, abort — tell the user: "Target directory
<path> already contains files; either choose an empty path or invoke tweaking-a-cast to modify what's there."
Inputs
Collected by divining-intent if invoked through divining-intent; otherwise ask directly:
| Input | Type | Default |
|---|
SERVICENAME | kebab-case identifier | (required) |
SERVICEPORT | string (numeric port) | 8080 |
SERVICESCHEMA | snake_case identifier | SERVICENAME with - → _ |
| Storage adapter | postgres / mysql / dynamodb / cosmos / memory | memory |
| Auth | none / jwt / jwt+roles | jwt |
| Multi-tenancy | yes / no | yes |
| Observability | off / prom / prom+otlp | prom+otlp |
| Leader election | yes / no | no |
| Pub/sub | yes / no | no |
Procedure
1. Clone the template
cp -r "${CLAUDE_PLUGIN_ROOT}/templates/service/." ./
mv go.mod.tmpl go.mod
mv README.md.tmpl README.md
2. Substitute placeholders
find . -type f \( -name '*.go' -o -name '*.yaml' -o -name '*.yml' -o -name 'Makefile' -o -name 'Dockerfile' -o -name 'go.mod' -o -name 'README.md' \) -exec sed -i.bak \
-e "s/SERVICENAME/${NAME}/g" \
-e "s/SERVICEPORT/${PORT}/g" \
-e "s/SERVICESCHEMA/${SCHEMA}/g" {} \;
find . -name '*.bak' -delete
3. Apply wizard-conditional edits
In cmd/server.go:
| Wizard answer | Edit |
|---|
multi-tenancy: no | Remove the line r.Use(middlewares.TenantRequestContext) |
auth: none | Remove r.Use(authMiddleware), the whole EnsureValidTokenConfig block, and middlewares.SetDefaultClaimsConfig(...). Drop the auth.* keys from config/default.yaml |
auth: jwt+roles | No code change here — the role guards land per-route via casting-a-feature or casting-a-route |
observability: off | Replace the middlewares.ObservabilityWithOptions(...) call with chi's middleware.Logger, remove the /metrics handler line, and delete cmd/observability.go. Drop the observability.* keys from config/default.yaml |
observability: prom | Set observability.tracing.enabled: false in config/default.yaml; keep prom + /metrics |
observability: prom+otlp | Default — leave as-is |
leader-election: yes | Insert this in runServer before initRoutes(...): leadership.NewLeaderElection(leadership.LeaderElectionProps{HeartbeatInterval: viper.GetDuration("leadership.heartbeat"), StorageAdapter: storageAdapter, AdditionalProps: map[string]any{}}).Start(). Add leadership.heartbeat: 5s to config/default.yaml. Add "github.com/tink3rlabs/magic/leadership" to imports |
pubsub: yes | Add "github.com/tink3rlabs/magic/pubsub" to imports; add stub initialization in runServer: // TODO: wire publisher/subscriber using magic/pubsub here (user finishes wiring) |
Storage adapter choice is applied via config/default.yaml. Use the storage.type key (value must match one of the constants: memory, sql, cosmosdb, dynamodb) and a flat storage.config map for connection details:
storage:
type: sql
config:
schema: myservice
host: localhost
port: "5432"
For non-memory choices, the wizard should have also asked the user for connection details; populate them under storage.config.* (flat map — not storage.<type>.host style nested keys).
4. Tidy + smoke-build
go mod tidy
go vet ./...
go build ./...
If any of these fail because the magic API at the pinned version differs from the template:
- Read the upstream URL from
magic-capabilities for the symbol that errored.
- Reconcile the template edit against the upstream signature.
- Re-run vet + build.
- If still failing, report DONE_WITH_CONCERNS with the specific reconciliation needed.
5. Commit
git init -q
git add .
git commit -m "feat: bootstrap ${NAME} service via spellcasting"
Verification
go run . server &
sleep 2
curl -s localhost:${PORT}/health/liveness
kill %1
Capability boundary
Out-of-bounds for this cast — if any of these appear in the generated tree, you've drifted; revert the offending file and use the in-bounds equivalent from magic-capabilities:
- Any project-specific "common" Go module that wraps magic
- Any custom auth, rate-limit, audit, or cache middleware not provided by magic
- Any per-resource ACL authorizer beyond
middlewares.RequireRole
In-bounds surface: see magic-capabilities.
Doc references (pinned to magic v0.17.3)