Spec-driven Internal Developer Platform built on the Argo ecosystem. Teams
declare what they want via a ServiceSpec; the platform decides how.
This is a portfolio piece for Staff Platform Engineer interviews. See CLAUDE.md for the design contract and ROADMAP.md for the milestone plan.
ServiceSpec CR ──► Go controller ──► Namespace + RBAC + Quota + ArgoCD Application
│
▼
ArgoCD syncs
workload from Git
- Controller (Go, kubebuilder) — reconciles
ServiceSpecCRs into platform primitives. - ArgoCD — handles Git-to-cluster workload sync within the namespace the controller created.
- Argo Workflows (Phase 2) — delegated task runner for finite jobs (image builds).
Decisions are recorded in docs/adr/. The pivot from "Workflows as reconciler" (ADR-004) to "controller as reconciler" (ADR-005) is the architectural cornerstone.
The local environment scaffold is up:
- k3d cluster with embedded registry
- ArgoCD installed via Kustomize + Helm
- Go module initialized (controller code lands in M1)
The controller itself is not yet scaffolded — that's Milestone 1.
- macOS Apple Silicon (arm64)
- Docker Desktop running
- Tools (install via Homebrew):
brew install go-task k3d kubectl helm kustomize| Tool | Pinned version |
|---|---|
| k3s image | rancher/k3s:v1.31.14-k3s1 |
| ArgoCD Helm chart | 9.5.7 (ArgoCD 3.3.8) |
| k3d | 5.8.x |
| go-task | 3.50.x |
task up # create cluster + registry, install ArgoCD, wait for ready
task status # show all pods across all namespaces
task down # tear everything downtask up is idempotent — running it twice is safe. The cluster
creation step is skipped if the cluster already exists; ArgoCD is
re-applied via kubectl apply --server-side --force-conflicts.
| Task | Description |
|---|---|
task up |
Create k3d cluster + registry, install ArgoCD, wait for ready |
task up:cluster |
Create cluster only (idempotent) |
task up:argocd |
Install/update ArgoCD only (idempotent) |
task down |
Delete cluster + registry |
task status |
kubectl get pods -A |
task build |
go build -o bin/argoplat . (placeholder until M1) |
task deploy |
Placeholder until M1 |
After task up:
- Cluster: 1 server node (k3s v1.31.14), 0 agents
- Ingress: Traefik (k3s built-in) on host ports 80/443
- Registry:
k3d-argoplat-registryon host port 5050 (see note below) - ArgoCD components in the
argocdnamespace:argocd-server(insecure mode, no TLS)argocd-application-controller(StatefulSet)argocd-repo-serverargocd-applicationset-controller(enabled for Git Generator)argocd-redis(standalone)argocd-notifications-controller- Dex is disabled (no SSO for local dev)
- CRDs:
applications.argoproj.io,applicationsets.argoproj.io,appprojects.argoproj.io
After task up, confirm:
kubectl get nodes # 1 node, Ready
kubectl get pods -n argocd # all Running
kubectl get pods -n kube-system | grep traefik
kubectl get crd | grep argoproj # 3 CRDs
docker ps | grep registry # registry on :5050Push a test image to the registry:
docker pull nginx:alpine
docker tag nginx:alpine k3d-argoplat-registry:5050/test:v1
docker push k3d-argoplat-registry:5050/test:v1Round-trip the cluster to prove idempotency:
task down && task up && task up # second up should be a no-op for clusterargoplat/
├── CLAUDE.md # design contract (read first)
├── ROADMAP.md # milestone plan
├── README.md # this file
├── Taskfile.yml # task runner
├── go.mod # Go module (controller in M1)
├── main.go # placeholder entry point
├── .gitignore
├── docs/
│ ├── adr/ # architecture decisions (000–005)
│ └── journal/ # development journal
└── infra/
├── k3d-config.yaml # cluster + registry definition
└── argocd/
├── kustomization.yaml # Helm chart inflator
└── values.yaml # laptop-tuned ArgoCD config
Two deviations from the original plan, worth knowing if you read the ADRs:
Registry on port 5050, not 5000. macOS uses port 5000 for the
AirPlay Receiver (ControlCenter). Rather than ask developers to disable
that system service, the registry binds to 5050. ADR-002's design
intent — a local registry that mirrors production CI/CD — is unchanged.
Image references use k3d-argoplat-registry:5050/....
kustomize build, not kubectl kustomize. The Kustomize version
bundled with kubectl v1.34 calls helm version -c --short to detect
Helm, but Helm v4 removed the -c flag. Standalone kustomize v5.8+
handles this correctly. The Taskfile uses standalone kustomize.
- Real CI/CD, multi-cluster, SSO, HPA, NetworkPolicies
- TLS at the ArgoCD server (Traefik is the termination point in production; local dev uses HTTP)
- Argo Workflows / Argo Events installation (Phase 2)
M1 — Controller scaffold: kubebuilder init, ServiceSpec CRD with
OpenAPI schema, basic Reconcile() that creates a Namespace from the
spec. See ROADMAP.md.