P0.1: Repository and module setup

Go module, Makefile with standard targets, golangci-lint v2 config,
CLAUDE.md, and empty CLI/agent binaries. Build, vet, and lint all pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-26 11:12:52 -07:00
parent 6a90b21a62
commit eaad18116a
9 changed files with 278 additions and 1 deletions

18
.gitignore vendored Normal file
View File

@@ -0,0 +1,18 @@
# Binaries
/mcp
/mcp-agent
# Runtime data
srv/
# Database files
*.db
*.db-wal
*.db-shm
# IDE
.idea/
.vscode/
# OS
.DS_Store

80
.golangci.yaml Normal file
View File

@@ -0,0 +1,80 @@
version: "2"
run:
timeout: 5m
tests: true
linters:
default: none
enable:
- errcheck
- govet
- ineffassign
- unused
- errorlint
- gosec
- staticcheck
- revive
settings:
errcheck:
check-blank: false
check-type-assertions: true
govet:
enable-all: true
disable:
- shadow
- fieldalignment
gosec:
severity: medium
confidence: medium
excludes:
- G104
errorlint:
errorf: true
asserts: true
comparison: true
revive:
rules:
- name: error-return
severity: error
- name: unexported-return
severity: error
- name: error-strings
severity: warning
- name: if-return
severity: warning
- name: increment-decrement
severity: warning
- name: var-naming
severity: warning
- name: range
severity: warning
- name: time-naming
severity: warning
- name: indent-error-flow
severity: warning
- name: early-return
severity: warning
formatters:
enable:
- gofmt
- goimports
issues:
max-issues-per-linter: 0
max-same-issues: 0
exclusions:
paths:
- vendor
rules:
- path: "_test\\.go"
linters:
- gosec
text: "G101"

58
CLAUDE.md Normal file
View File

@@ -0,0 +1,58 @@
# CLAUDE.md
## Project Overview
MCP (Metacircular Control Plane) is the orchestrator for the Metacircular platform. It manages container lifecycle, tracks what services run where, and transfers files between the operator's workstation and managed nodes.
## Architecture
MCP has two components:
- **CLI** (`mcp`) — thin client on the operator's workstation. Reads local service definition files, pushes intent to agents, queries status. No database.
- **Agent** (`mcp-agent`) — smart per-node daemon. Manages containers via podman, stores the registry (SQLite), monitors for drift, alerts the operator.
Services have one or more components (containers). Container naming: `<service>-<component>`.
## Build Commands
```bash
make all # vet → lint → test → build both binaries
make build # go build ./...
make test # go test ./...
make vet # go vet ./...
make lint # golangci-lint run ./...
make proto # regenerate gRPC code from .proto files
make proto-lint # buf lint + buf breaking
make mcp # build CLI binary
make mcp-agent # build agent binary
make clean # remove binaries
```
Run a single test: `go test ./internal/registry/ -run TestComponentCRUD`
## Project Structure
- `cmd/mcp/` — CLI entry point
- `cmd/mcp-agent/` — Agent entry point
- `internal/agent/` — Agent core (deploy, lifecycle, sync, adopt, status, files)
- `internal/runtime/` — Container runtime abstraction (podman)
- `internal/registry/` — SQLite registry (services, components, events)
- `internal/monitor/` — Monitoring subsystem (watch loop, alerting)
- `internal/servicedef/` — Service definition file parsing (TOML)
- `internal/auth/` — MCIAS integration (token validation, interceptor)
- `internal/config/` — Configuration loading (CLI + agent)
- `proto/mcp/v1/` — Proto definitions
- `gen/mcp/v1/` — Generated Go gRPC code
## Critical Rules
1. Agent is gRPC-only (no REST). This is a deliberate exception to the platform's REST+gRPC parity rule.
2. Container naming convention: `<service>-<component>` (e.g., `metacrypt-api`, `metacrypt-web`).
3. File operations are scoped to `/srv/<service>/`. Path traversal is rejected.
4. Alert commands use exec-style invocation (argv array, no shell). Never use `sh -c`.
5. The agent binds to the overlay interface only, not all interfaces. It does NOT sit behind MC-Proxy.
6. Every RPC is audit-logged at info level (method, caller, timestamp).
7. `active: true/false` in service definitions controls desired state. `mcp stop/start` update the file.
## Module Path
`git.wntrmute.dev/kyle/mcp`

35
Makefile Normal file
View File

@@ -0,0 +1,35 @@
.PHONY: build test vet lint proto proto-lint clean all
LDFLAGS := -trimpath -ldflags="-s -w -X main.version=$(shell git describe --tags --always --dirty)"
mcp:
CGO_ENABLED=0 go build $(LDFLAGS) -o mcp ./cmd/mcp
mcp-agent:
CGO_ENABLED=0 go build $(LDFLAGS) -o mcp-agent ./cmd/mcp-agent
build:
go build ./...
test:
go test ./...
vet:
go vet ./...
lint:
golangci-lint run ./...
proto:
protoc --go_out=. --go_opt=module=git.wntrmute.dev/kyle/mcp \
--go-grpc_out=. --go-grpc_opt=module=git.wntrmute.dev/kyle/mcp \
proto/mcp/v1/*.proto
proto-lint:
buf lint
buf breaking --against '.git#branch=master,subdir=proto'
clean:
rm -f mcp mcp-agent
all: vet lint test mcp mcp-agent

View File

@@ -2,7 +2,7 @@
## Phase 0: Project Scaffolding ## Phase 0: Project Scaffolding
- [ ] **P0.1** Repository and module setup - [x] **P0.1** Repository and module setup
- [ ] **P0.2** Proto definitions and code generation - [ ] **P0.2** Proto definitions and code generation
## Phase 1: Core Libraries ## Phase 1: Core Libraries

33
cmd/mcp-agent/main.go Normal file
View File

@@ -0,0 +1,33 @@
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var (
version = "dev"
cfgPath string
)
func main() {
root := &cobra.Command{
Use: "mcp-agent",
Short: "Metacircular Control Plane agent",
}
root.PersistentFlags().StringVarP(&cfgPath, "config", "c", "", "config file path")
root.AddCommand(&cobra.Command{
Use: "version",
Short: "Print version",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(version)
},
})
if err := root.Execute(); err != nil {
os.Exit(1)
}
}

33
cmd/mcp/main.go Normal file
View File

@@ -0,0 +1,33 @@
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var (
version = "dev"
cfgPath string
)
func main() {
root := &cobra.Command{
Use: "mcp",
Short: "Metacircular Control Plane CLI",
}
root.PersistentFlags().StringVarP(&cfgPath, "config", "c", "", "config file path")
root.AddCommand(&cobra.Command{
Use: "version",
Short: "Print version",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(version)
},
})
if err := root.Execute(); err != nil {
os.Exit(1)
}
}

10
go.mod Normal file
View File

@@ -0,0 +1,10 @@
module git.wntrmute.dev/kyle/mcp
go 1.25.7
require github.com/spf13/cobra v1.10.2
require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.9 // indirect
)

10
go.sum Normal file
View File

@@ -0,0 +1,10 @@
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=