diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 2e6b603..083beff 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -1,6 +1,6 @@ # .gitea/workflows/ci.yaml # ============================================ -# Switchboard Core - CI/CD Pipeline (v0.17.3) +# Armature - CI/CD Pipeline (v0.17.3) # ============================================ # Single unified image (Go backend + nginx frontend). # v0.1.0: Dropped FE/BE image split per ROADMAP design decision. @@ -29,22 +29,22 @@ # Tags (v*) → always full pipeline # # Deployment mapping (single domain, path-based): -# PR → FE + BE :dev → switchboard.DOMAIN/dev/ (DB wipe + fresh schema) -# Push to main → FE + BE :test → switchboard.DOMAIN/test/ (migrate only) -# Tag v* → FE + BE :latest → switchboard.DOMAIN/ (migrate only) +# PR → FE + BE :dev → armature.DOMAIN/dev/ (DB wipe + fresh schema) +# Push to main → FE + BE :test → armature.DOMAIN/test/ (migrate only) +# Tag v* → FE + BE :latest → armature.DOMAIN/ (migrate only) # → Unified :latest → Docker Hub (not deployed) # # Database lifecycle: # CI: bootstrap (admin creds) → create DB + role + extensions # CI: dev wipe (app creds) → drop tables for fresh install test # BE: auto-migrate on startup → schema_migrations tracking -# BE: bootstrap admin from SWITCHBOARD_ADMIN_* env vars (upsert on every restart) +# BE: bootstrap admin from ARMATURE_ADMIN_* env vars (upsert on every restart) # # Shared PG safety: # - Bootstrap uses IF NOT EXISTS (idempotent) # - Dev wipe REFUSES to run on databases not ending in _dev # - Admin creds never reach the backend pods -# - Each env has its own DB name (switchboard_core_{dev,test,}) +# - Each env has its own DB name (armature_{dev,test,}) # # Required Gitea Variables: # REGISTRY, NAMESPACE, DOMAIN, POSTGRES_HOST @@ -58,7 +58,7 @@ # Required Gitea Secrets: # POSTGRES_USER, POSTGRES_PASSWORD # POSTGRES_ADMIN_USER, POSTGRES_ADMIN_PASSWORD -# SWITCHBOARD_ADMIN_USERNAME, SWITCHBOARD_ADMIN_PASSWORD, SWITCHBOARD_ADMIN_EMAIL +# ARMATURE_ADMIN_USERNAME, ARMATURE_ADMIN_PASSWORD, ARMATURE_ADMIN_EMAIL # ENCRYPTION_KEY — AES-256 key for API key encryption (openssl rand -base64 32) # PROVIDER_KEY — API key for live provider integration tests (optional, tests skip if missing) # DOCKERHUB_USERNAME, DOCKERHUB_TOKEN (optional) @@ -88,8 +88,8 @@ env: CERT_ISSUER: ${{ vars.CERT_ISSUER_PROD || 'letsencrypt-prod' }} POSTGRES_HOST: ${{ vars.POSTGRES_HOST || 'postgres-primary.postgres.svc.cluster.local' }} POSTGRES_PORT: ${{ vars.POSTGRES_PORT || '5432' }} - IMAGE: ${{ vars.REGISTRY }}/switchboard/core - DOCKERHUB_IMAGE: ${{ vars.DOCKERHUB_IMAGE || 'gobha/switchboard-core' }} + IMAGE: ${{ vars.REGISTRY }}/armature + DOCKERHUB_IMAGE: ${{ vars.DOCKERHUB_IMAGE || 'gobha/armature' }} jobs: # ── Stage 0: Detect Changed Paths ────────────── @@ -319,7 +319,7 @@ jobs: PGPASSWORD: ${{ secrets.POSTGRES_ADMIN_PASSWORD }} APP_USER: ${{ secrets.POSTGRES_USER }} APP_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} - DB_NAME: switchboard_core_ci + DB_NAME: armature_ci run: | echo "━━━ CI Test Database Setup ━━━" # Create DB for Go integration tests (admin creds) @@ -363,7 +363,7 @@ jobs: PGUSER: ${{ secrets.POSTGRES_ADMIN_USER }} PGPASSWORD: ${{ secrets.POSTGRES_ADMIN_PASSWORD }} run: | - psql -c "DROP DATABASE IF EXISTS switchboard_core_ci;" postgres + psql -c "DROP DATABASE IF EXISTS armature_ci;" postgres echo "✓ Dropped CI test database" # ── Stage 2: Build, Database, Deploy ───────── @@ -395,9 +395,9 @@ jobs: - name: Determine environment id: setup run: | - # All environments share one host: switchboard.DOMAIN + # All environments share one host: armature.DOMAIN # Environments are separated by path prefix (BASE_PATH) - DEPLOY_HOST="switchboard.${DOMAIN}" + DEPLOY_HOST="armature.${DOMAIN}" echo "DEPLOY_HOST=${DEPLOY_HOST}" >> "$GITHUB_OUTPUT" if [[ "${{ gitea.event_name }}" == "pull_request" ]]; then @@ -406,7 +406,7 @@ jobs: echo "IMAGE_TAG=dev" >> "$GITHUB_OUTPUT" echo "BASE_PATH=/dev" >> "$GITHUB_OUTPUT" echo "DEPLOY_SUFFIX=-dev" >> "$GITHUB_OUTPUT" - echo "DB_NAME=switchboard_core_dev" >> "$GITHUB_OUTPUT" + echo "DB_NAME=armature_dev" >> "$GITHUB_OUTPUT" echo "DB_WIPE=true" >> "$GITHUB_OUTPUT" echo "REPLICAS=1" >> "$GITHUB_OUTPUT" echo "MEMORY_REQUEST=256Mi" >> "$GITHUB_OUTPUT" @@ -422,7 +422,7 @@ jobs: echo "EXTRA_TAG=${VERSION}" >> "$GITHUB_OUTPUT" echo "BASE_PATH=" >> "$GITHUB_OUTPUT" echo "DEPLOY_SUFFIX=" >> "$GITHUB_OUTPUT" - echo "DB_NAME=switchboard_core" >> "$GITHUB_OUTPUT" + echo "DB_NAME=armature" >> "$GITHUB_OUTPUT" echo "DB_WIPE=false" >> "$GITHUB_OUTPUT" echo "REPLICAS=2" >> "$GITHUB_OUTPUT" echo "MEMORY_REQUEST=256Mi" >> "$GITHUB_OUTPUT" @@ -437,7 +437,7 @@ jobs: echo "IMAGE_TAG=test" >> "$GITHUB_OUTPUT" echo "BASE_PATH=/test" >> "$GITHUB_OUTPUT" echo "DEPLOY_SUFFIX=-test" >> "$GITHUB_OUTPUT" - echo "DB_NAME=switchboard_core_test" >> "$GITHUB_OUTPUT" + echo "DB_NAME=armature_test" >> "$GITHUB_OUTPUT" echo "DB_WIPE=false" >> "$GITHUB_OUTPUT" echo "REPLICAS=1" >> "$GITHUB_OUTPUT" echo "MEMORY_REQUEST=256Mi" >> "$GITHUB_OUTPUT" @@ -579,14 +579,17 @@ jobs: # ── Push to Docker Hub (release only) ──────── - name: Push to Docker Hub if: steps.setup.outputs.is_release == 'true' + env: + DH_USER: ${{ secrets.DOCKERHUB_USERNAME }} + DH_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} run: | docker tag ${IMAGE}:${{ steps.setup.outputs.IMAGE_TAG }} ${DOCKERHUB_IMAGE}:latest docker tag ${IMAGE}:${{ steps.setup.outputs.IMAGE_TAG }} ${DOCKERHUB_IMAGE}:${{ steps.setup.outputs.EXTRA_TAG }} - if [[ -n "${{ secrets.DOCKERHUB_TOKEN }}" ]]; then - echo "${{ secrets.DOCKERHUB_TOKEN }}" | \ - docker login -u "${{ secrets.DOCKERHUB_USERNAME }}" --password-stdin + if [[ -n "${DH_TOKEN}" ]]; then + echo "${DH_TOKEN}" | docker login -u "${DH_USER}" --password-stdin docker push ${DOCKERHUB_IMAGE}:latest docker push ${DOCKERHUB_IMAGE}:${{ steps.setup.outputs.EXTRA_TAG }} + echo "✓ Pushed to Docker Hub" else echo "⚠ Docker Hub credentials not configured, skipping push" fi @@ -600,31 +603,31 @@ jobs: env: PG_USER: ${{ secrets.POSTGRES_USER }} PG_PASS: ${{ secrets.POSTGRES_PASSWORD }} - SW_ADMIN_USER: ${{ secrets.SWITCHBOARD_ADMIN_USERNAME }} - SW_ADMIN_PASS: ${{ secrets.SWITCHBOARD_ADMIN_PASSWORD }} - SW_ADMIN_EMAIL: ${{ secrets.SWITCHBOARD_ADMIN_EMAIL }} + SW_ADMIN_USER: ${{ secrets.ARMATURE_ADMIN_USERNAME }} + SW_ADMIN_PASS: ${{ secrets.ARMATURE_ADMIN_PASSWORD }} + SW_ADMIN_EMAIL: ${{ secrets.ARMATURE_ADMIN_EMAIL }} SEED_USERS: ${{ vars.SEED_USERS }} ENCRYPTION_KEY: ${{ secrets.ENCRYPTION_KEY }} run: | - kubectl create secret generic switchboard-db-credentials \ + kubectl create secret generic armature-db-credentials \ --namespace=${NAMESPACE} \ --from-literal=POSTGRES_USER="${PG_USER}" \ --from-literal=POSTGRES_PASSWORD="${PG_PASS}" \ --dry-run=client -o yaml | kubectl apply -f - - kubectl create secret generic switchboard-admin \ + kubectl create secret generic armature-admin \ --namespace=${NAMESPACE} \ --from-literal=username="${SW_ADMIN_USER}" \ --from-literal=password="${SW_ADMIN_PASS}" \ --from-literal=email="${SW_ADMIN_EMAIL}" \ --dry-run=client -o yaml | kubectl apply -f - - kubectl create secret generic switchboard-seed-users \ + kubectl create secret generic armature-seed-users \ --namespace=${NAMESPACE} \ --from-literal=users="${SEED_USERS}" \ --dry-run=client -o yaml | kubectl apply -f - - kubectl create secret generic switchboard-encryption \ + kubectl create secret generic armature-encryption \ --namespace=${NAMESPACE} \ --from-literal=ENCRYPTION_KEY="${ENCRYPTION_KEY}" \ --dry-run=client -o yaml | kubectl apply -f - @@ -639,7 +642,7 @@ jobs: S3_REGION: ${{ vars.S3_REGION || 'us-east-1' }} S3_PREFIX: ${{ vars.S3_PREFIX }} run: | - kubectl create secret generic switchboard-s3 \ + kubectl create secret generic armature-s3 \ --namespace=${NAMESPACE} \ --from-literal=S3_ENDPOINT="${S3_ENDPOINT}" \ --from-literal=S3_BUCKET="${S3_BUCKET}" \ @@ -673,7 +676,7 @@ jobs: envsubst < k8s/storage-pvc.yaml > /tmp/storage-pvc.yaml fi - envsubst < k8s/switchboard.yaml > /tmp/switchboard.yaml + envsubst < k8s/armature.yaml > /tmp/armature.yaml envsubst < k8s/middleware-retry.yaml > /tmp/middleware-retry.yaml envsubst < k8s/ingress.yaml > /tmp/ingress.yaml @@ -687,7 +690,7 @@ jobs: echo " ⚠ STORAGE_CLASS not set — file storage disabled" fi - kubectl apply -f /tmp/switchboard.yaml + kubectl apply -f /tmp/armature.yaml # Traefik retry middleware (v0.28.8) — requires RBAC for traefik.io CRDs. # First-time setup: kubectl apply -f k8s/rbac-traefik.yaml (cluster admin) @@ -706,8 +709,8 @@ jobs: # Traefik invalidates the entire router if it references a missing middleware, # which kills all routes for this path prefix. if [[ "${MW_READY}" == "true" ]]; then - MW_REF="${NAMESPACE}-switchboard-retry${DEPLOY_SUFFIX}@kubernetescrd" - kubectl annotate ingress "switchboard${DEPLOY_SUFFIX}" \ + MW_REF="${NAMESPACE}-armature-retry${DEPLOY_SUFFIX}@kubernetescrd" + kubectl annotate ingress "armature${DEPLOY_SUFFIX}" \ "traefik.ingress.kubernetes.io/router.middlewares=${MW_REF}" \ --namespace="${NAMESPACE}" --overwrite echo " ✓ Ingress annotated with retry middleware" @@ -718,20 +721,20 @@ jobs: SUFFIX: ${{ steps.setup.outputs.DEPLOY_SUFFIX }} ENV: ${{ steps.setup.outputs.ENVIRONMENT }} run: | - kubectl rollout restart deployment/switchboard${SUFFIX} -n ${NAMESPACE} - kubectl rollout status deployment/switchboard${SUFFIX} -n ${NAMESPACE} --timeout=180s + kubectl rollout restart deployment/armature${SUFFIX} -n ${NAMESPACE} + kubectl rollout status deployment/armature${SUFFIX} -n ${NAMESPACE} --timeout=180s echo "" echo "━━━ Pod Status ━━━" - kubectl get pods -n ${NAMESPACE} -l app=switchboard,env=${ENV} + kubectl get pods -n ${NAMESPACE} -l app=armature,env=${ENV} - READY=$(kubectl get deployment switchboard${SUFFIX} -n ${NAMESPACE} -o jsonpath='{.status.readyReplicas}') + READY=$(kubectl get deployment armature${SUFFIX} -n ${NAMESPACE} -o jsonpath='{.status.readyReplicas}') if [[ "${READY:-0}" -gt 0 ]]; then echo "✅ Healthy: ${READY} pods ready" else echo "❌ Unhealthy: ${READY:-0} pods ready" - kubectl logs -n ${NAMESPACE} -l app=switchboard,env=${ENV} --tail=30 + kubectl logs -n ${NAMESPACE} -l app=armature,env=${ENV} --tail=30 exit 1 fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d53d84..c9a99a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -All notable changes to Switchboard Core are documented here. +All notable changes to Armature are documented here. ## v0.6.7 — Native mTLS @@ -28,7 +28,7 @@ deployments where the Go binary terminates TLS itself. - **Peer TLS config**: `BuildPeerTLSConfig()` constructs a `*tls.Config` for outbound node-to-node connections (forward-looking — cluster registry is currently DB-backed with no HTTP peer calls). -- **`switchboard-ca.sh`**: Shell wrapper around openssl for cert provisioning. +- **`armature-ca.sh`**: Shell wrapper around openssl for cert provisioning. Three commands: `init` (CA keypair), `issue-node` (365d, ServerAuth + ClientAuth EKU), `issue-user` (90d, ClientAuth only). All ECDSA P-256, PEM output. @@ -875,7 +875,7 @@ Indexes on `folder_id`, `creator_id`, `pinned`, `updated_at`. - **Migration 012**: Adds `bundled` to `packages.source` CHECK constraint (both Postgres and SQLite). - **K8s manifest**: `SKIP_BUNDLED_PACKAGES` and `BUNDLED_PACKAGES` env vars - added to `k8s/switchboard.yaml`. + added to `k8s/armature.yaml`. - **Tests**: 6 handler tests (fresh install, skip existing, missing dir, empty dir, dormant handling, allowlist filtering). @@ -1339,7 +1339,7 @@ Indexes on `folder_id`, `creator_id`, `pinned`, `updated_at`. (`"triggers": [{"type": "event", "pattern": "workflow.completed", ...}]`). Wired via `bus.Subscribe()` on startup. Handlers fire asynchronously. - **Webhook triggers**: Inbound HTTP at `/api/v1/hooks/:package_id/:slug`. - HMAC-SHA256 verification via `X-Switchboard-Signature` header. Synchronous + HMAC-SHA256 verification via `X-Armature-Signature` header. Synchronous Starlark handler can return custom HTTP status and body. - **Scheduled tasks**: User-created cron-scheduled Starlark scripts with restricted sandbox (no raw HTTP, no DB table creation, connections-only @@ -1492,21 +1492,21 @@ storage, and the Starlark sandbox. Everything else is a package. - CI deploy: k8s resource quantity vars (`BE_MEMORY_REQUEST` → `MEMORY_REQUEST`) aligned with CI workflow outputs — `envsubst` was producing empty strings - CI deploy: image var (`BE_IMAGE` → `IMAGE`) — caused `InvalidImageName` in pods -- CI rollout: deployment name (`switchboard` → `switchboard-be`) — rollout +- CI rollout: deployment name (`switchboard` → `armature-be`) — rollout verification was looking for wrong deployment name - Nginx BASE_PATH: regex cache-header locations intercepted static asset requests before alias could strip the sub-path prefix — moved inside alias block - Post-login blank page: dead Go template references (`surface-chat`, `surface-notes`, `surface-projects`) caused html/template to silently produce Content-Length: 0 responses -- Login branding: "Chat Switchboard" → "Switchboard Core", updated tagline +- Login branding: "Chat Armature" → "Armature", updated tagline and feature pills to reflect platform pivot ### Changed -- Go module: `switchboard-core` +- Go module: `armature` - VERSION: `0.1.0` -- Default DB name: `switchboard_core` +- Default DB name: `armature` - Fresh migrations: 9 files × 2 dialects (postgres + sqlite), 27 tables - Store interfaces: 40 → 20 (13 in interfaces.go + 7 in separate iface files) - Stage modes: `chat_only` removed, `custom` added @@ -1514,7 +1514,7 @@ storage, and the Starlark sandbox. Everything else is a package. - Kernel permissions: 16 → 6 (`extension.use`, `extension.install`, `workflow.create`, `workflow.submit`, `admin.view`, `token.unlimited`) - Everyone group seed: `["extension.use","workflow.submit"]` -- Global settings seed: site name "Switchboard Core" +- Global settings seed: site name "Armature" - Config: removed 7 dropped fields (SessionExpiryDays, WorkflowStaleHours, ProviderAutoDisableThreshold, ExtractionConcurrency, etc.) - Health stores rewritten: kernel-only Prune for stale tickets, counters, presence diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9135d16..c9d02d5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contributing to Switchboard Core +# Contributing to Armature ## Development Setup @@ -8,8 +8,8 @@ ```sh cd server -go build -o switchboard-core . -DB_DRIVER=sqlite DATABASE_URL=/tmp/switchboard.db ./switchboard-core +go build -o armature . +DB_DRIVER=sqlite DATABASE_URL=/tmp/armature.db ./armature ``` **Docker (recommended):** diff --git a/Dockerfile b/Dockerfile index 0392981..8e7071a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # ============================================ -# Switchboard Core — Unified Dockerfile +# Armature — Unified Dockerfile # ============================================ # Stage 1: Build Go backend # Stage 2: Download JS vendor libs (marked, DOMPurify) @@ -20,7 +20,7 @@ COPY server/go.mod server/go.sum* ./ COPY server/ . COPY VERSION ./ RUN go mod download && go mod tidy && go mod verify -RUN CGO_ENABLED=0 go build -ldflags="-s -w -X main.Version=$(cat VERSION)" -o /bin/switchboard . +RUN CGO_ENABLED=0 go build -ldflags="-s -w -X main.Version=$(cat VERSION)" -o /bin/armature . # ── Stage 2: Vendor JS libs ───────────────── FROM node:20-alpine AS vendor @@ -70,7 +70,7 @@ FROM nginx:1-alpine RUN apk add --no-cache bash git # Go backend binary -COPY --from=backend /bin/switchboard /usr/local/bin/switchboard +COPY --from=backend /bin/armature /usr/local/bin/armature COPY --from=backend /app/database/migrations /app/database/migrations # Frontend static files diff --git a/Dockerfile.builder b/Dockerfile.builder index f26e4d8..71c938f 100644 --- a/Dockerfile.builder +++ b/Dockerfile.builder @@ -1,5 +1,5 @@ # ============================================ -# Switchboard Core — Builder Image +# Armature — Builder Image # ============================================ # Pre-caches Go modules and Node dependencies # for faster custom builds. Use as a base in @@ -7,12 +7,12 @@ # download on every build. # # Usage: -# FROM ghcr.io/switchboard-core/builder:latest AS go-builder +# FROM ghcr.io/armature/builder:latest AS go-builder # COPY server/ /app/ -# RUN cd /app && go build -o /bin/switchboard . +# RUN cd /app && go build -o /bin/armature . # # Or build this image locally: -# docker build -f Dockerfile.builder -t switchboard-builder . +# docker build -f Dockerfile.builder -t armature-builder . # ============================================ # ── Go module cache ───────────────────────── @@ -61,5 +61,5 @@ RUN ln -sf /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm WORKDIR /build -LABEL org.opencontainers.image.title="Switchboard Core Builder" -LABEL org.opencontainers.image.description="Pre-cached build dependencies for faster custom Switchboard Core builds" +LABEL org.opencontainers.image.title="Armature Builder" +LABEL org.opencontainers.image.description="Pre-cached build dependencies for faster custom Armature builds" diff --git a/README.md b/README.md index 4da38f5..0054ffb 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# Switchboard Core +# Armature A self-hosted extension platform. Identity, teams, permissions, workflows, and a package system. Everything else ships as installable extensions. ## What This Is -Switchboard Core is the kernel. It provides the primitives that extensions +Armature is the kernel. It provides the primitives that extensions build on: users, teams, groups, scoped credentials, a Starlark sandbox, workflow orchestration, notifications, and a package installer. It does not include AI chat, providers, personas, or any domain-specific features — @@ -27,7 +27,7 @@ docker compose up --build # → http://localhost:3000 (admin/admin) # Or from source -git clone && cd switchboard-core +git clone && cd armature cp server/.env.example server/.env # edit DB credentials cd server && go run . # → http://localhost:8080 diff --git a/ROADMAP.md b/ROADMAP.md index 677b1b5..2c94b71 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,4 +1,4 @@ -# Switchboard Core — Roadmap +# Armature — Roadmap ## Current: v0.6.7 — Native mTLS @@ -135,7 +135,7 @@ End-to-end mutual TLS without a reverse proxy. Targets systemd+podman deployment | TLS server mode | ✅ | Go binary calls `ListenAndServeTLS` directly. `TLS_CERT`, `TLS_KEY`, `TLS_CA` path config. TLS 1.3 minimum, no fallback. `server/config/tls.go` loader + validation. | | `MTLSNativeProvider` | ✅ | Reads `r.TLS.PeerCertificates[0]` — no header trust. `Subject.CommonName` → username. `sha256(Raw)` → `external_id`. Auto-provisions `auth_source=mtls`. Renamed existing `mtls.go` → `mtls_proxy.go`. Shared helpers in `mtls_helpers.go`. | | Node-to-node mTLS | ✅ | `BuildPeerTLSConfig()` constructs outbound TLS config with node cert + CA pool. Forward-looking — cluster registry is DB-backed (PG LISTEN/NOTIFY), no HTTP peer calls yet. | -| `switchboard-ca.sh` | ✅ | Shell wrapper around openssl. Three commands: `init` (CA keypair) · `issue-node --name --san ` (365d) · `issue-user --cn ` (90d). All output PEM. | +| `armature-ca.sh` | ✅ | Shell wrapper around openssl. Three commands: `init` (CA keypair) · `issue-node --name --san ` (365d) · `issue-user --cn ` (90d). All output PEM. | | Unit tests | ✅ | Ephemeral CA via `crypto/x509`. Fabricated `PeerCertificates`. Valid cert → user provisioned · no TLS → `ErrNoCert` · empty certs → `ErrNoCert` · no CN → `ErrInvalidCreds`. | | Integration tests | ✅ | Real TLS listener on localhost. No cert / wrong CA / expired → TLS handshake rejected. Valid cert → 200. Peer certificate CN + email visibility verified. | diff --git a/chart/Chart.yaml b/chart/Chart.yaml index fa41040..1d48761 100644 --- a/chart/Chart.yaml +++ b/chart/Chart.yaml @@ -1,11 +1,11 @@ apiVersion: v2 -name: switchboard -description: Switchboard Core — self-hosted extension platform +name: armature +description: Armature — self-hosted extension platform type: application version: 0.1.0 appVersion: "0.0.0" # Patched at CI/release time from /VERSION home: https://gobha.ai sources: - - https://git.gobha.me/switchboard/core + - https://git.gobha.me/armature/core maintainers: - name: xcaliber diff --git a/chart/alerting/switchboard-rules.yaml b/chart/alerting/armature-rules.yaml similarity index 62% rename from chart/alerting/switchboard-rules.yaml rename to chart/alerting/armature-rules.yaml index 7a86bee..46d5bda 100644 --- a/chart/alerting/switchboard-rules.yaml +++ b/chart/alerting/armature-rules.yaml @@ -1,27 +1,27 @@ -# Switchboard Core — PrometheusRule alerts (v0.33.0) +# Armature Core — PrometheusRule alerts (v0.33.0) # Source file for the Helm template. Deploy via: # monitoring.prometheusRule.enabled: true apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: - name: switchboard-alerts + name: armature-alerts spec: groups: - - name: switchboard.rules + - name: armature.rules rules: # Pod restart (possible OOM) - - alert: SwitchboardPodRestart + - alert: ArmaturePodRestart expr: increase(kube_pod_container_status_restarts_total{container="backend"}[1h]) > 0 for: 0m labels: severity: warning annotations: - summary: "Switchboard backend pod restarted (possible OOM)" + summary: "Armature backend pod restarted (possible OOM)" description: "Container {{ $labels.container }} in pod {{ $labels.pod }} restarted." # Provider down for 5+ minutes - - alert: SwitchboardProviderDown - expr: switchboard_provider_status > 2 + - alert: ArmatureProviderDown + expr: armature_provider_status > 2 for: 5m labels: severity: critical @@ -29,8 +29,8 @@ spec: summary: "Provider {{ $labels.provider_config_id }} is down" # DB pool >80% utilized - - alert: SwitchboardDBPoolExhaustion - expr: switchboard_db_in_use_connections / switchboard_db_open_connections > 0.8 + - alert: ArmatureDBPoolExhaustion + expr: armature_db_in_use_connections / armature_db_open_connections > 0.8 for: 5m labels: severity: warning @@ -38,10 +38,10 @@ spec: summary: "DB connection pool >80% utilized" # HTTP 5xx error rate >5% - - alert: SwitchboardHighErrorRate + - alert: ArmatureHighErrorRate expr: > - sum(rate(switchboard_http_requests_total{status=~"5.."}[5m])) - / sum(rate(switchboard_http_requests_total[5m])) > 0.05 + sum(rate(armature_http_requests_total{status=~"5.."}[5m])) + / sum(rate(armature_http_requests_total[5m])) > 0.05 for: 5m labels: severity: warning @@ -49,10 +49,10 @@ spec: summary: "HTTP 5xx error rate exceeds 5%" # Task failure rate >25% - - alert: SwitchboardTaskFailureRate + - alert: ArmatureTaskFailureRate expr: > - rate(switchboard_task_executions_total{status="error"}[15m]) - / rate(switchboard_task_executions_total[15m]) > 0.25 + rate(armature_task_executions_total{status="error"}[15m]) + / rate(armature_task_executions_total[15m]) > 0.25 for: 10m labels: severity: warning @@ -60,8 +60,8 @@ spec: summary: "Task failure rate exceeds 25% over 15 minutes" # No completions processed in 15 minutes (canary) - - alert: SwitchboardNoCompletions - expr: sum(rate(switchboard_completions_total[10m])) == 0 + - alert: ArmatureNoCompletions + expr: sum(rate(armature_completions_total[10m])) == 0 for: 15m labels: severity: critical diff --git a/chart/dashboards/switchboard-overview.json b/chart/dashboards/armature-overview.json similarity index 72% rename from chart/dashboards/switchboard-overview.json rename to chart/dashboards/armature-overview.json index 67192a5..1e93ca0 100644 --- a/chart/dashboards/switchboard-overview.json +++ b/chart/dashboards/armature-overview.json @@ -17,10 +17,10 @@ { "type": "panel", "id": "gauge", "name": "Gauge", "version": "" } ], "id": null, - "uid": "switchboard-overview", - "title": "Switchboard Core — Overview", + "uid": "armature-overview", + "title": "Armature — Overview", "description": "System overview: request rates, latency, provider health, token usage, DB pool.", - "tags": ["switchboard"], + "tags": ["armature"], "timezone": "browser", "refresh": "30s", "schemaVersion": 38, @@ -37,7 +37,7 @@ "name": "namespace", "type": "query", "datasource": { "type": "prometheus", "uid": "${datasource}" }, - "query": "label_values(switchboard_http_requests_total, namespace)", + "query": "label_values(armature_http_requests_total, namespace)", "includeAll": true, "current": { "text": "All", "value": "$__all" } }, @@ -45,7 +45,7 @@ "name": "pod", "type": "query", "datasource": { "type": "prometheus", "uid": "${datasource}" }, - "query": "label_values(switchboard_http_requests_total{namespace=~\"$namespace\"}, pod)", + "query": "label_values(armature_http_requests_total{namespace=~\"$namespace\"}, pod)", "includeAll": true, "current": { "text": "All", "value": "$__all" } } @@ -58,7 +58,7 @@ "gridPos": { "h": 8, "w": 6, "x": 0, "y": 0 }, "targets": [ { - "expr": "sum(rate(switchboard_http_requests_total{namespace=~\"$namespace\"}[5m]))", + "expr": "sum(rate(armature_http_requests_total{namespace=~\"$namespace\"}[5m]))", "legendFormat": "Total req/s" } ] @@ -69,7 +69,7 @@ "gridPos": { "h": 8, "w": 6, "x": 6, "y": 0 }, "targets": [ { - "expr": "sum(rate(switchboard_http_requests_total{namespace=~\"$namespace\",status=~\"5..\"}[5m])) / sum(rate(switchboard_http_requests_total{namespace=~\"$namespace\"}[5m]))", + "expr": "sum(rate(armature_http_requests_total{namespace=~\"$namespace\",status=~\"5..\"}[5m])) / sum(rate(armature_http_requests_total{namespace=~\"$namespace\"}[5m]))", "legendFormat": "5xx rate" } ], @@ -93,15 +93,15 @@ "gridPos": { "h": 8, "w": 6, "x": 12, "y": 0 }, "targets": [ { - "expr": "histogram_quantile(0.50, sum(rate(switchboard_http_request_duration_seconds_bucket{namespace=~\"$namespace\"}[5m])) by (le))", + "expr": "histogram_quantile(0.50, sum(rate(armature_http_request_duration_seconds_bucket{namespace=~\"$namespace\"}[5m])) by (le))", "legendFormat": "p50" }, { - "expr": "histogram_quantile(0.95, sum(rate(switchboard_http_request_duration_seconds_bucket{namespace=~\"$namespace\"}[5m])) by (le))", + "expr": "histogram_quantile(0.95, sum(rate(armature_http_request_duration_seconds_bucket{namespace=~\"$namespace\"}[5m])) by (le))", "legendFormat": "p95" }, { - "expr": "histogram_quantile(0.99, sum(rate(switchboard_http_request_duration_seconds_bucket{namespace=~\"$namespace\"}[5m])) by (le))", + "expr": "histogram_quantile(0.99, sum(rate(armature_http_request_duration_seconds_bucket{namespace=~\"$namespace\"}[5m])) by (le))", "legendFormat": "p99" } ], @@ -113,7 +113,7 @@ "gridPos": { "h": 8, "w": 6, "x": 18, "y": 0 }, "targets": [ { - "expr": "sum(switchboard_websocket_connections{namespace=~\"$namespace\"})", + "expr": "sum(armature_websocket_connections{namespace=~\"$namespace\"})", "legendFormat": "Active" } ] @@ -124,7 +124,7 @@ "gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 }, "targets": [ { - "expr": "sum by (provider_config_id) (rate(switchboard_completions_total{namespace=~\"$namespace\"}[5m]))", + "expr": "sum by (provider_config_id) (rate(armature_completions_total{namespace=~\"$namespace\"}[5m]))", "legendFormat": "{{provider_config_id}}" } ] @@ -135,7 +135,7 @@ "gridPos": { "h": 8, "w": 12, "x": 12, "y": 8 }, "targets": [ { - "expr": "histogram_quantile(0.95, sum by (provider_config_id, le) (rate(switchboard_completion_duration_seconds_bucket{namespace=~\"$namespace\"}[5m])))", + "expr": "histogram_quantile(0.95, sum by (provider_config_id, le) (rate(armature_completion_duration_seconds_bucket{namespace=~\"$namespace\"}[5m])))", "legendFormat": "{{provider_config_id}}" } ], @@ -147,7 +147,7 @@ "gridPos": { "h": 8, "w": 12, "x": 0, "y": 16 }, "targets": [ { - "expr": "sum by (model_id) (rate(switchboard_completion_tokens_total{namespace=~\"$namespace\"}[5m])) * 60", + "expr": "sum by (model_id) (rate(armature_completion_tokens_total{namespace=~\"$namespace\"}[5m])) * 60", "legendFormat": "{{model_id}}" } ] @@ -158,7 +158,7 @@ "gridPos": { "h": 8, "w": 12, "x": 12, "y": 16 }, "targets": [ { - "expr": "switchboard_provider_status{namespace=~\"$namespace\"}", + "expr": "armature_provider_status{namespace=~\"$namespace\"}", "legendFormat": "{{provider_config_id}}" } ], @@ -179,15 +179,15 @@ "gridPos": { "h": 8, "w": 12, "x": 0, "y": 24 }, "targets": [ { - "expr": "switchboard_db_open_connections{namespace=~\"$namespace\"}", + "expr": "armature_db_open_connections{namespace=~\"$namespace\"}", "legendFormat": "Open" }, { - "expr": "switchboard_db_in_use_connections{namespace=~\"$namespace\"}", + "expr": "armature_db_in_use_connections{namespace=~\"$namespace\"}", "legendFormat": "In Use" }, { - "expr": "switchboard_db_idle_connections{namespace=~\"$namespace\"}", + "expr": "armature_db_idle_connections{namespace=~\"$namespace\"}", "legendFormat": "Idle" } ] @@ -198,7 +198,7 @@ "gridPos": { "h": 8, "w": 12, "x": 12, "y": 24 }, "targets": [ { - "expr": "sum by (status) (rate(switchboard_task_executions_total{namespace=~\"$namespace\"}[5m]))", + "expr": "sum by (status) (rate(armature_task_executions_total{namespace=~\"$namespace\"}[5m]))", "legendFormat": "{{status}}" } ] diff --git a/chart/templates/NOTES.txt b/chart/templates/NOTES.txt index 04c688a..0a71e01 100644 --- a/chart/templates/NOTES.txt +++ b/chart/templates/NOTES.txt @@ -1,4 +1,4 @@ -Chat Switchboard {{ .Chart.AppVersion }} deployed. +Armature {{ .Chart.AppVersion }} deployed. {{- if .Values.ingress.enabled }} Access the application at: diff --git a/chart/templates/_helpers.tpl b/chart/templates/_helpers.tpl index 90681d7..4fff3de 100644 --- a/chart/templates/_helpers.tpl +++ b/chart/templates/_helpers.tpl @@ -1,7 +1,7 @@ {{/* Common labels */}} -{{- define "switchboard.labels" -}} +{{- define "armature.labels" -}} app.kubernetes.io/name: {{ .Chart.Name }} app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} @@ -12,23 +12,23 @@ helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} {{/* Backend selector labels */}} -{{- define "switchboard.backend.labels" -}} +{{- define "armature.backend.labels" -}} app.kubernetes.io/component: backend -{{ include "switchboard.labels" . }} +{{ include "armature.labels" . }} {{- end }} {{/* Frontend selector labels */}} -{{- define "switchboard.frontend.labels" -}} +{{- define "armature.frontend.labels" -}} app.kubernetes.io/component: frontend -{{ include "switchboard.labels" . }} +{{ include "armature.labels" . }} {{- end }} {{/* Secret name */}} -{{- define "switchboard.secretName" -}} +{{- define "armature.secretName" -}} {{- if .Values.existingSecret -}} {{ .Values.existingSecret }} {{- else -}} @@ -39,21 +39,21 @@ Secret name {{/* Backend image */}} -{{- define "switchboard.backend.image" -}} +{{- define "armature.backend.image" -}} {{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag | default .Chart.AppVersion }} {{- end }} {{/* Frontend image */}} -{{- define "switchboard.frontend.image" -}} +{{- define "armature.frontend.image" -}} {{ .Values.frontend.image.repository }}:{{ .Values.frontend.image.tag | default .Chart.AppVersion }} {{- end }} {{/* Database URL — assembled from postgres.* if url is empty */}} -{{- define "switchboard.databaseURL" -}} +{{- define "armature.databaseURL" -}} {{- if .Values.database.url -}} {{ .Values.database.url }} {{- else if eq .Values.database.driver "sqlite" -}} diff --git a/chart/templates/configmap.yaml b/chart/templates/configmap.yaml index aeeac52..047a066 100644 --- a/chart/templates/configmap.yaml +++ b/chart/templates/configmap.yaml @@ -3,7 +3,7 @@ kind: ConfigMap metadata: name: {{ .Release.Name }}-config labels: - {{- include "switchboard.labels" . | nindent 4 }} + {{- include "armature.labels" . | nindent 4 }} data: PORT: {{ .Values.backend.port | quote }} BASE_PATH: {{ .Values.basePath | quote }} diff --git a/chart/templates/cronjob-backup.yaml b/chart/templates/cronjob-backup.yaml index 0002f17..f0210ad 100644 --- a/chart/templates/cronjob-backup.yaml +++ b/chart/templates/cronjob-backup.yaml @@ -4,7 +4,7 @@ kind: CronJob metadata: name: {{ .Release.Name }}-backup labels: - {{- include "switchboard.labels" . | nindent 4 }} + {{- include "armature.labels" . | nindent 4 }} app.kubernetes.io/component: backup spec: schedule: {{ .Values.backup.schedule | quote }} @@ -35,7 +35,7 @@ spec: - | set -e TIMESTAMP=$(date +%Y%m%d-%H%M%S) - FILENAME="switchboard-${TIMESTAMP}.sql.gz" + FILENAME="armature-${TIMESTAMP}.sql.gz" echo "Starting backup: ${FILENAME}" pg_dump "${DATABASE_URL}" | gzip > "/backup/${FILENAME}" @@ -58,22 +58,22 @@ spec: # Prune old local backups beyond retention cd /backup - ls -t switchboard-*.sql.gz 2>/dev/null | tail -n +{{ add1 .Values.backup.retention }} | xargs -r rm -v + ls -t armature-*.sql.gz 2>/dev/null | tail -n +{{ add1 .Values.backup.retention }} | xargs -r rm -v echo "Backup complete" env: - name: DATABASE_URL - value: {{ include "switchboard.databaseURL" . | quote }} + value: {{ include "armature.databaseURL" . | quote }} {{- if .Values.backup.s3.enabled }} - name: S3_ACCESS_KEY valueFrom: secretKeyRef: - name: {{ include "switchboard.secretName" . }} + name: {{ include "armature.secretName" . }} key: BACKUP_S3_ACCESS_KEY optional: true - name: S3_SECRET_KEY valueFrom: secretKeyRef: - name: {{ include "switchboard.secretName" . }} + name: {{ include "armature.secretName" . }} key: BACKUP_S3_SECRET_KEY optional: true {{- end }} diff --git a/chart/templates/deployment-backend.yaml b/chart/templates/deployment-backend.yaml index c5f194e..14a4041 100644 --- a/chart/templates/deployment-backend.yaml +++ b/chart/templates/deployment-backend.yaml @@ -3,7 +3,7 @@ kind: Deployment metadata: name: {{ .Release.Name }}-backend labels: - {{- include "switchboard.backend.labels" . | nindent 4 }} + {{- include "armature.backend.labels" . | nindent 4 }} spec: replicas: {{ .Values.backend.replicaCount }} selector: @@ -38,7 +38,7 @@ spec: topologyKey: kubernetes.io/hostname containers: - name: backend - image: {{ include "switchboard.backend.image" . }} + image: {{ include "armature.backend.image" . }} imagePullPolicy: {{ .Values.backend.image.pullPolicy }} ports: - name: http @@ -48,10 +48,10 @@ spec: - configMapRef: name: {{ .Release.Name }}-config - secretRef: - name: {{ include "switchboard.secretName" . }} + name: {{ include "armature.secretName" . }} env: - name: DATABASE_URL - value: {{ include "switchboard.databaseURL" . | quote }} + value: {{ include "armature.databaseURL" . | quote }} resources: {{- toYaml .Values.backend.resources | nindent 12 }} {{- if .Values.persistence.enabled }} diff --git a/chart/templates/deployment-frontend.yaml b/chart/templates/deployment-frontend.yaml index 9f0c90b..d9161e4 100644 --- a/chart/templates/deployment-frontend.yaml +++ b/chart/templates/deployment-frontend.yaml @@ -3,7 +3,7 @@ kind: Deployment metadata: name: {{ .Release.Name }}-frontend labels: - {{- include "switchboard.frontend.labels" . | nindent 4 }} + {{- include "armature.frontend.labels" . | nindent 4 }} spec: replicas: {{ .Values.frontend.replicaCount }} selector: @@ -24,7 +24,7 @@ spec: {{- end }} containers: - name: frontend - image: {{ include "switchboard.frontend.image" . }} + image: {{ include "armature.frontend.image" . }} imagePullPolicy: {{ .Values.frontend.image.pullPolicy }} ports: - name: http diff --git a/chart/templates/grafana-dashboard-configmap.yaml b/chart/templates/grafana-dashboard-configmap.yaml index 7cd5d1c..da2df69 100644 --- a/chart/templates/grafana-dashboard-configmap.yaml +++ b/chart/templates/grafana-dashboard-configmap.yaml @@ -2,13 +2,13 @@ apiVersion: v1 kind: ConfigMap metadata: - name: {{ include "switchboard.fullname" . }}-grafana-dashboard + name: {{ include "armature.fullname" . }}-grafana-dashboard labels: - {{- include "switchboard.labels" . | nindent 4 }} + {{- include "armature.labels" . | nindent 4 }} {{- with .Values.monitoring.grafanaDashboard.labels }} {{- toYaml . | nindent 4 }} {{- end }} data: - switchboard-overview.json: |- - {{- .Files.Get "dashboards/switchboard-overview.json" | nindent 4 }} + armature-overview.json: |- + {{- .Files.Get "dashboards/armature-overview.json" | nindent 4 }} {{- end }} diff --git a/chart/templates/ingress.yaml b/chart/templates/ingress.yaml index 93222d2..a373d2e 100644 --- a/chart/templates/ingress.yaml +++ b/chart/templates/ingress.yaml @@ -4,7 +4,7 @@ kind: Ingress metadata: name: {{ .Release.Name }}-ingress labels: - {{- include "switchboard.labels" . | nindent 4 }} + {{- include "armature.labels" . | nindent 4 }} {{- if or .Values.ingress.annotations (and (eq (default "traefik" .Values.ingress.className) "traefik") .Values.ingress.retry.annotateIngress) }} annotations: {{- with .Values.ingress.annotations }} diff --git a/chart/templates/middleware-retry.yaml b/chart/templates/middleware-retry.yaml index ca75884..9aef2c2 100644 --- a/chart/templates/middleware-retry.yaml +++ b/chart/templates/middleware-retry.yaml @@ -5,7 +5,7 @@ kind: Middleware metadata: name: {{ .Release.Name }}-retry labels: - {{- include "switchboard.labels" . | nindent 4 }} + {{- include "armature.labels" . | nindent 4 }} spec: retry: attempts: {{ .Values.ingress.retry.attempts | default 2 }} diff --git a/chart/templates/prometheusrule.yaml b/chart/templates/prometheusrule.yaml index 9a08373..dcc93cb 100644 --- a/chart/templates/prometheusrule.yaml +++ b/chart/templates/prometheusrule.yaml @@ -2,57 +2,57 @@ apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: - name: {{ include "switchboard.fullname" . }}-alerts + name: {{ include "armature.fullname" . }}-alerts labels: - {{- include "switchboard.labels" . | nindent 4 }} + {{- include "armature.labels" . | nindent 4 }} {{- with .Values.monitoring.prometheusRule.labels }} {{- toYaml . | nindent 4 }} {{- end }} spec: groups: - - name: switchboard.rules + - name: armature.rules rules: - - alert: SwitchboardPodRestart + - alert: ArmaturePodRestart expr: increase(kube_pod_container_status_restarts_total{container="backend"}[1h]) > 0 for: 0m labels: severity: warning annotations: - summary: "Switchboard backend pod restarted (possible OOM)" - - alert: SwitchboardProviderDown - expr: switchboard_provider_status > 2 + summary: "Armature backend pod restarted (possible OOM)" + - alert: ArmatureProviderDown + expr: armature_provider_status > 2 for: 5m labels: severity: critical annotations: summary: "Provider {{`{{ $labels.provider_config_id }}`}} is down" - - alert: SwitchboardDBPoolExhaustion - expr: switchboard_db_in_use_connections / switchboard_db_open_connections > 0.8 + - alert: ArmatureDBPoolExhaustion + expr: armature_db_in_use_connections / armature_db_open_connections > 0.8 for: 5m labels: severity: warning annotations: summary: "DB connection pool >80% utilized" - - alert: SwitchboardHighErrorRate + - alert: ArmatureHighErrorRate expr: | - sum(rate(switchboard_http_requests_total{status=~"5.."}[5m])) - / sum(rate(switchboard_http_requests_total[5m])) > 0.05 + sum(rate(armature_http_requests_total{status=~"5.."}[5m])) + / sum(rate(armature_http_requests_total[5m])) > 0.05 for: 5m labels: severity: warning annotations: summary: "HTTP 5xx error rate exceeds 5%" - - alert: SwitchboardTaskFailureRate + - alert: ArmatureTaskFailureRate expr: | - rate(switchboard_task_executions_total{status="error"}[15m]) - / rate(switchboard_task_executions_total[15m]) > 0.25 + rate(armature_task_executions_total{status="error"}[15m]) + / rate(armature_task_executions_total[15m]) > 0.25 for: 10m labels: severity: warning annotations: summary: "Task failure rate exceeds 25%" - - alert: SwitchboardNoCompletions - expr: sum(rate(switchboard_completions_total[10m])) == 0 + - alert: ArmatureNoCompletions + expr: sum(rate(armature_completions_total[10m])) == 0 for: 15m labels: severity: critical diff --git a/chart/templates/pvc-backup.yaml b/chart/templates/pvc-backup.yaml index d9076a2..79cca64 100644 --- a/chart/templates/pvc-backup.yaml +++ b/chart/templates/pvc-backup.yaml @@ -4,7 +4,7 @@ kind: PersistentVolumeClaim metadata: name: {{ .Release.Name }}-backup labels: - {{- include "switchboard.labels" . | nindent 4 }} + {{- include "armature.labels" . | nindent 4 }} app.kubernetes.io/component: backup spec: accessModes: diff --git a/chart/templates/pvc.yaml b/chart/templates/pvc.yaml index beeb9be..e0625a7 100644 --- a/chart/templates/pvc.yaml +++ b/chart/templates/pvc.yaml @@ -4,7 +4,7 @@ kind: PersistentVolumeClaim metadata: name: {{ .Release.Name }}-data labels: - {{- include "switchboard.labels" . | nindent 4 }} + {{- include "armature.labels" . | nindent 4 }} spec: accessModes: - {{ .Values.persistence.accessMode }} diff --git a/chart/templates/secret.yaml b/chart/templates/secret.yaml index 0f6b490..418f0a4 100644 --- a/chart/templates/secret.yaml +++ b/chart/templates/secret.yaml @@ -4,7 +4,7 @@ kind: Secret metadata: name: {{ .Release.Name }}-secrets labels: - {{- include "switchboard.labels" . | nindent 4 }} + {{- include "armature.labels" . | nindent 4 }} type: Opaque stringData: JWT_SECRET: {{ required "jwtSecret is required" .Values.jwtSecret | quote }} @@ -15,11 +15,11 @@ stringData: POSTGRES_PASSWORD: {{ .Values.database.postgres.password | quote }} {{- end }} {{- if .Values.admin.password }} - SWITCHBOARD_ADMIN_USERNAME: {{ .Values.admin.username | quote }} - SWITCHBOARD_ADMIN_PASSWORD: {{ .Values.admin.password | quote }} + ARMATURE_ADMIN_USERNAME: {{ .Values.admin.username | quote }} + ARMATURE_ADMIN_PASSWORD: {{ .Values.admin.password | quote }} {{- end }} {{- if .Values.admin.email }} - SWITCHBOARD_ADMIN_EMAIL: {{ .Values.admin.email | quote }} + ARMATURE_ADMIN_EMAIL: {{ .Values.admin.email | quote }} {{- end }} {{- if .Values.storage.s3.accessKey }} S3_ACCESS_KEY: {{ .Values.storage.s3.accessKey | quote }} diff --git a/chart/templates/servicemonitor.yaml b/chart/templates/servicemonitor.yaml index 308b30b..90cc18a 100644 --- a/chart/templates/servicemonitor.yaml +++ b/chart/templates/servicemonitor.yaml @@ -2,16 +2,16 @@ apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: - name: {{ include "switchboard.fullname" . }} + name: {{ include "armature.fullname" . }} labels: - {{- include "switchboard.labels" . | nindent 4 }} + {{- include "armature.labels" . | nindent 4 }} {{- with .Values.monitoring.serviceMonitor.labels }} {{- toYaml . | nindent 4 }} {{- end }} spec: selector: matchLabels: - {{- include "switchboard.selectorLabels" . | nindent 6 }} + {{- include "armature.selectorLabels" . | nindent 6 }} app.kubernetes.io/component: backend endpoints: - port: http diff --git a/chart/templates/services.yaml b/chart/templates/services.yaml index 8886496..fa7b618 100644 --- a/chart/templates/services.yaml +++ b/chart/templates/services.yaml @@ -3,7 +3,7 @@ kind: Service metadata: name: {{ .Release.Name }}-backend labels: - {{- include "switchboard.backend.labels" . | nindent 4 }} + {{- include "armature.backend.labels" . | nindent 4 }} annotations: prometheus.io/scrape: "true" prometheus.io/port: {{ .Values.backend.port | quote }} @@ -25,7 +25,7 @@ kind: Service metadata: name: {{ .Release.Name }}-frontend labels: - {{- include "switchboard.frontend.labels" . | nindent 4 }} + {{- include "armature.frontend.labels" . | nindent 4 }} spec: type: ClusterIP ports: diff --git a/chart/values.yaml b/chart/values.yaml index 2edb5a8..18a2f54 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -1,10 +1,10 @@ -# Switchboard Core — Helm values -# helm install switchboard ./chart +# Armature — Helm values +# helm install armature ./chart # ── Images ───────────────────────────────── backend: image: - repository: git.gobha.me/switchboard/core + repository: git.gobha.me/armature/core tag: "" # defaults to Chart.appVersion pullPolicy: IfNotPresent replicaCount: 2 # v0.32.0: multi-replica HA @@ -19,7 +19,7 @@ backend: frontend: image: - repository: git.gobha.me/switchboard/core + repository: git.gobha.me/armature/core tag: "" # defaults to Chart.appVersion pullPolicy: IfNotPresent replicaCount: 1 @@ -41,12 +41,12 @@ database: postgres: host: postgresql port: 5432 - user: switchboard + user: armature password: "" # set via secret - database: switchboard + database: armature sslmode: disable # For sqlite: path inside the container (requires persistence) - sqlitePath: /data/switchboard.db + sqlitePath: /data/armature.db # ── Core ─────────────────────────────────── basePath: "" # URL prefix, e.g. "/dev" @@ -85,7 +85,7 @@ ingress: enabled: true className: traefik annotations: {} - host: switchboard.local + host: armature.local tls: enabled: false secretName: "" diff --git a/ci/e2e-cluster-test.sh b/ci/e2e-cluster-test.sh index 18048ca..9f48e00 100755 --- a/ci/e2e-cluster-test.sh +++ b/ci/e2e-cluster-test.sh @@ -103,7 +103,7 @@ done # ── Test 4: Stop one replica → stale sweep ──── echo "=== Test 4: Stop node-3, wait for sweep ===" -docker compose -f docker-compose-e2e.yml stop switchboard-3 +docker compose -f docker-compose-e2e.yml stop armature-3 echo " waiting ${STALE_WAIT}s for stale sweep..." sleep "$STALE_WAIT" @@ -118,7 +118,7 @@ fi # ── Test 5: Restart replica → re-registers ──── echo "=== Test 5: Restart node-3 ===" -docker compose -f docker-compose-e2e.yml start switchboard-3 +docker compose -f docker-compose-e2e.yml start armature-3 # Wait for startup + at least one heartbeat for i in $(seq 1 30); do diff --git a/ci/e2e-nginx.conf b/ci/e2e-nginx.conf index 7231890..5b8389a 100644 --- a/ci/e2e-nginx.conf +++ b/ci/e2e-nginx.conf @@ -3,10 +3,10 @@ events { } http { - upstream switchboard { - server switchboard-1:80; - server switchboard-2:80; - server switchboard-3:80; + upstream armature { + server armature-1:80; + server armature-2:80; + server armature-3:80; } # WebSocket upgrade map @@ -19,7 +19,7 @@ http { listen 80; location / { - proxy_pass http://switchboard; + proxy_pass http://armature; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -28,7 +28,7 @@ http { # WebSocket endpoint location /ws { - proxy_pass http://switchboard; + proxy_pass http://armature; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; diff --git a/ci/e2e-upgrade-rolling.sh b/ci/e2e-upgrade-rolling.sh index 619d599..e2afb94 100755 --- a/ci/e2e-upgrade-rolling.sh +++ b/ci/e2e-upgrade-rolling.sh @@ -83,11 +83,11 @@ authed() { echo -e "\n${YELLOW}═══ Phase 1: Build Images ═══${NC}" echo "Building 'old' image from HEAD..." -docker build -t switchboard-core:v-old . -q +docker build -t armature:v-old . -q ok "old image built" echo "Building 'new' image from working tree..." -docker build -t switchboard-core:v-new . -q +docker build -t armature:v-new . -q ok "new image built" # ═══════════════════════════════════════════════ @@ -97,7 +97,7 @@ ok "new image built" echo -e "\n${YELLOW}═══ Phase 2: Start Cluster (Old Version) ═══${NC}" # Override the build with old image -SWITCHBOARD_IMAGE=switchboard-core:v-old \ +ARMATURE_IMAGE=armature:v-old \ docker compose -f "$COMPOSE_FILE" up postgres -d # Wait for postgres @@ -107,30 +107,30 @@ sleep 3 docker run -d --name sb-old-1 --network "$(basename "$(pwd)")_default" \ -e PORT=8080 -e BASE_PATH="" \ -e DB_DRIVER=postgres \ - -e "DATABASE_URL=postgres://switchboard:e2e-password@postgres:5432/switchboard_e2e?sslmode=disable" \ + -e "DATABASE_URL=postgres://armature:e2e-password@postgres:5432/armature_e2e?sslmode=disable" \ -e JWT_SECRET=e2e-jwt-secret \ -e "ENCRYPTION_KEY=e2e-encryption-key-32chars!!!!!" \ - -e SWITCHBOARD_ADMIN_USERNAME=admin \ - -e SWITCHBOARD_ADMIN_PASSWORD=admin \ + -e ARMATURE_ADMIN_USERNAME=admin \ + -e ARMATURE_ADMIN_PASSWORD=admin \ -e STORAGE_BACKEND=pvc -e STORAGE_PATH=/data/storage \ -e "CORS_ALLOWED_ORIGINS=*" -e EXT_ALLOW_PRIVATE_IPS=true \ -e LOG_FORMAT=text -e LOG_LEVEL=info \ -e "SEED_USERS=alice:password123:user,bob:password456:user" \ - -p 8081:80 switchboard-core:v-old 2>/dev/null || true + -p 8081:80 armature:v-old 2>/dev/null || true docker run -d --name sb-old-2 --network "$(basename "$(pwd)")_default" \ -e PORT=8080 -e BASE_PATH="" \ -e DB_DRIVER=postgres \ - -e "DATABASE_URL=postgres://switchboard:e2e-password@postgres:5432/switchboard_e2e?sslmode=disable" \ + -e "DATABASE_URL=postgres://armature:e2e-password@postgres:5432/armature_e2e?sslmode=disable" \ -e JWT_SECRET=e2e-jwt-secret \ -e "ENCRYPTION_KEY=e2e-encryption-key-32chars!!!!!" \ - -e SWITCHBOARD_ADMIN_USERNAME=admin \ - -e SWITCHBOARD_ADMIN_PASSWORD=admin \ + -e ARMATURE_ADMIN_USERNAME=admin \ + -e ARMATURE_ADMIN_PASSWORD=admin \ -e STORAGE_BACKEND=pvc -e STORAGE_PATH=/data/storage \ -e "CORS_ALLOWED_ORIGINS=*" -e EXT_ALLOW_PRIVATE_IPS=true \ -e LOG_FORMAT=text -e LOG_LEVEL=info \ -e "SEED_USERS=alice:password123:user,bob:password456:user" \ - -p 8082:80 switchboard-core:v-old 2>/dev/null || true + -p 8082:80 armature:v-old 2>/dev/null || true # Start nginx LB docker compose -f "$COMPOSE_FILE" up lb -d @@ -189,16 +189,16 @@ echo "Starting new replica-1..." docker run -d --name sb-new-1 --network "$(basename "$(pwd)")_default" \ -e PORT=8080 -e BASE_PATH="" \ -e DB_DRIVER=postgres \ - -e "DATABASE_URL=postgres://switchboard:e2e-password@postgres:5432/switchboard_e2e?sslmode=disable" \ + -e "DATABASE_URL=postgres://armature:e2e-password@postgres:5432/armature_e2e?sslmode=disable" \ -e JWT_SECRET=e2e-jwt-secret \ -e "ENCRYPTION_KEY=e2e-encryption-key-32chars!!!!!" \ - -e SWITCHBOARD_ADMIN_USERNAME=admin \ - -e SWITCHBOARD_ADMIN_PASSWORD=admin \ + -e ARMATURE_ADMIN_USERNAME=admin \ + -e ARMATURE_ADMIN_PASSWORD=admin \ -e STORAGE_BACKEND=pvc -e STORAGE_PATH=/data/storage \ -e "CORS_ALLOWED_ORIGINS=*" -e EXT_ALLOW_PRIVATE_IPS=true \ -e LOG_FORMAT=text -e LOG_LEVEL=info \ -e "SEED_USERS=alice:password123:user,bob:password456:user" \ - -p 8081:80 switchboard-core:v-new + -p 8081:80 armature:v-new wait_for_health "$R1" "new replica-1" @@ -246,16 +246,16 @@ echo "Starting new replica-2..." docker run -d --name sb-new-2 --network "$(basename "$(pwd)")_default" \ -e PORT=8080 -e BASE_PATH="" \ -e DB_DRIVER=postgres \ - -e "DATABASE_URL=postgres://switchboard:e2e-password@postgres:5432/switchboard_e2e?sslmode=disable" \ + -e "DATABASE_URL=postgres://armature:e2e-password@postgres:5432/armature_e2e?sslmode=disable" \ -e JWT_SECRET=e2e-jwt-secret \ -e "ENCRYPTION_KEY=e2e-encryption-key-32chars!!!!!" \ - -e SWITCHBOARD_ADMIN_USERNAME=admin \ - -e SWITCHBOARD_ADMIN_PASSWORD=admin \ + -e ARMATURE_ADMIN_USERNAME=admin \ + -e ARMATURE_ADMIN_PASSWORD=admin \ -e STORAGE_BACKEND=pvc -e STORAGE_PATH=/data/storage \ -e "CORS_ALLOWED_ORIGINS=*" -e EXT_ALLOW_PRIVATE_IPS=true \ -e LOG_FORMAT=text -e LOG_LEVEL=info \ -e "SEED_USERS=alice:password123:user,bob:password456:user" \ - -p 8082:80 switchboard-core:v-new + -p 8082:80 armature:v-new wait_for_health "$R2" "new replica-2" diff --git a/ci/e2e-upgrade-test.sh b/ci/e2e-upgrade-test.sh index c6504af..ae22fde 100755 --- a/ci/e2e-upgrade-test.sh +++ b/ci/e2e-upgrade-test.sh @@ -82,13 +82,13 @@ echo -e "\n${YELLOW}═══ Phase 1: Build Images ═══${NC}" # Build "old" image from last commit (before current changes) echo "Building 'old' image from HEAD commit..." git stash -q 2>/dev/null || true -docker build --no-cache -t switchboard-core:v-old . -q +docker build --no-cache -t armature:v-old . -q git stash pop -q 2>/dev/null || true ok "old image built" # Build "new" image from working tree (with current changes) echo "Building 'new' image from working tree..." -docker build --no-cache -t core-switchboard-new . -q +docker build --no-cache -t armature:v-new . -q ok "new image built" # ═══════════════════════════════════════════════ @@ -97,7 +97,7 @@ ok "new image built" echo -e "\n${YELLOW}═══ Phase 2: Seed Data on Old Version ═══${NC}" -docker compose -f "$COMPOSE_FILE" up postgres switchboard-old -d +docker compose -f "$COMPOSE_FILE" up postgres armature-old -d wait_for_health "$HOST" # Auth @@ -179,17 +179,17 @@ ok "recorded $PRE_PKG_COUNT installed packages" echo -e "\n${YELLOW}═══ Phase 3: Upgrade ═══${NC}" echo "Stopping old version..." -docker compose -f "$COMPOSE_FILE" stop switchboard-old +docker compose -f "$COMPOSE_FILE" stop armature-old ok "old version stopped" echo "Starting new version..." -docker compose -f "$COMPOSE_FILE" up switchboard-new -d +docker compose -f "$COMPOSE_FILE" up armature-new -d wait_for_health "$HOST" ok "new version started" # Check for migration errors in logs echo -e "\n${YELLOW}Checking startup logs...${NC}" -LOGS=$(docker compose -f "$COMPOSE_FILE" logs switchboard-new 2>&1 || echo "") +LOGS=$(docker compose -f "$COMPOSE_FILE" logs armature-new 2>&1 || echo "") if echo "$LOGS" | grep -qi "migration.*fail\|schema.*error\|panic\|fatal"; then fail "migration errors found in logs" echo "$LOGS" | grep -i "migration\|schema\|panic\|fatal" | head -5 diff --git a/ci/keycloak-realm.json b/ci/keycloak-realm.json index 7730c0a..dcbfe57 100644 --- a/ci/keycloak-realm.json +++ b/ci/keycloak-realm.json @@ -1,5 +1,5 @@ { - "realm": "switchboard", + "realm": "armature", "enabled": true, "registrationAllowed": false, "loginWithEmailAllowed": true, @@ -9,11 +9,11 @@ "realm": [ { "name": "sb-admin", - "description": "Switchboard admin role" + "description": "Armature admin role" }, { "name": "sb-user", - "description": "Switchboard regular user role" + "description": "Armature regular user role" } ] }, @@ -29,11 +29,11 @@ ], "clients": [ { - "clientId": "switchboard", - "name": "Switchboard Core", + "clientId": "armature", + "name": "Armature", "enabled": true, "publicClient": false, - "secret": "switchboard-secret", + "secret": "armature-secret", "redirectUris": [ "http://localhost:3000/*", "http://localhost:8080/*" @@ -69,7 +69,7 @@ "users": [ { "username": "alice", - "email": "alice@switchboard.test", + "email": "alice@armature.test", "firstName": "Alice", "lastName": "Engineer", "enabled": true, @@ -83,7 +83,7 @@ ], "realmRoles": [ "sb-user", - "default-roles-switchboard" + "default-roles-armature" ], "groups": [ "/engineering" @@ -91,7 +91,7 @@ }, { "username": "bob", - "email": "bob@switchboard.test", + "email": "bob@armature.test", "firstName": "Bob", "lastName": "Lead", "enabled": true, @@ -106,7 +106,7 @@ "realmRoles": [ "sb-admin", "sb-user", - "default-roles-switchboard" + "default-roles-armature" ], "groups": [ "/engineering", diff --git a/docker-compose-e2e.yml b/docker-compose-e2e.yml index e3f66ab..1ba168b 100644 --- a/docker-compose-e2e.yml +++ b/docker-compose-e2e.yml @@ -1,6 +1,6 @@ # docker-compose-e2e.yml — Multi-replica E2E testing (Postgres) # -# Three Switchboard replicas behind an nginx load balancer, +# Three Armature replicas behind an nginx load balancer, # sharing a single Postgres instance. Tests cross-replica # broadcast via pg_notify and cluster registry (v0.6.0). # @@ -14,18 +14,18 @@ services: postgres: image: postgres:16-alpine environment: - POSTGRES_DB: switchboard_e2e - POSTGRES_USER: switchboard + POSTGRES_DB: armature_e2e + POSTGRES_USER: armature POSTGRES_PASSWORD: e2e-password ports: - "5432:5432" healthcheck: - test: ["CMD-SHELL", "pg_isready -U switchboard -d switchboard_e2e"] + test: ["CMD-SHELL", "pg_isready -U armature -d armature_e2e"] interval: 2s timeout: 5s retries: 10 - switchboard-1: + armature-1: build: context: . dockerfile: Dockerfile @@ -33,11 +33,11 @@ services: PORT: "8080" BASE_PATH: "" DB_DRIVER: postgres - DATABASE_URL: postgres://switchboard:e2e-password@postgres:5432/switchboard_e2e?sslmode=disable + DATABASE_URL: postgres://armature:e2e-password@postgres:5432/armature_e2e?sslmode=disable JWT_SECRET: e2e-jwt-secret ENCRYPTION_KEY: e2e-encryption-key-32chars!!!!! - SWITCHBOARD_ADMIN_USERNAME: admin - SWITCHBOARD_ADMIN_PASSWORD: admin + ARMATURE_ADMIN_USERNAME: admin + ARMATURE_ADMIN_PASSWORD: admin STORAGE_BACKEND: pvc STORAGE_PATH: /data/storage CORS_ALLOWED_ORIGINS: "*" @@ -48,14 +48,14 @@ services: CLUSTER_NODE_ID: "node-1" CLUSTER_HEARTBEAT_INTERVAL: "5s" CLUSTER_STALE_THRESHOLD: "15s" - CLUSTER_ENDPOINT: "http://switchboard-1:8080" + CLUSTER_ENDPOINT: "http://armature-1:8080" depends_on: postgres: condition: service_healthy ports: - "8081:80" - switchboard-2: + armature-2: build: context: . dockerfile: Dockerfile @@ -63,11 +63,11 @@ services: PORT: "8080" BASE_PATH: "" DB_DRIVER: postgres - DATABASE_URL: postgres://switchboard:e2e-password@postgres:5432/switchboard_e2e?sslmode=disable + DATABASE_URL: postgres://armature:e2e-password@postgres:5432/armature_e2e?sslmode=disable JWT_SECRET: e2e-jwt-secret ENCRYPTION_KEY: e2e-encryption-key-32chars!!!!! - SWITCHBOARD_ADMIN_USERNAME: admin - SWITCHBOARD_ADMIN_PASSWORD: admin + ARMATURE_ADMIN_USERNAME: admin + ARMATURE_ADMIN_PASSWORD: admin STORAGE_BACKEND: pvc STORAGE_PATH: /data/storage CORS_ALLOWED_ORIGINS: "*" @@ -78,14 +78,14 @@ services: CLUSTER_NODE_ID: "node-2" CLUSTER_HEARTBEAT_INTERVAL: "5s" CLUSTER_STALE_THRESHOLD: "15s" - CLUSTER_ENDPOINT: "http://switchboard-2:8080" + CLUSTER_ENDPOINT: "http://armature-2:8080" depends_on: postgres: condition: service_healthy ports: - "8082:80" - switchboard-3: + armature-3: build: context: . dockerfile: Dockerfile @@ -93,11 +93,11 @@ services: PORT: "8080" BASE_PATH: "" DB_DRIVER: postgres - DATABASE_URL: postgres://switchboard:e2e-password@postgres:5432/switchboard_e2e?sslmode=disable + DATABASE_URL: postgres://armature:e2e-password@postgres:5432/armature_e2e?sslmode=disable JWT_SECRET: e2e-jwt-secret ENCRYPTION_KEY: e2e-encryption-key-32chars!!!!! - SWITCHBOARD_ADMIN_USERNAME: admin - SWITCHBOARD_ADMIN_PASSWORD: admin + ARMATURE_ADMIN_USERNAME: admin + ARMATURE_ADMIN_PASSWORD: admin STORAGE_BACKEND: pvc STORAGE_PATH: /data/storage CORS_ALLOWED_ORIGINS: "*" @@ -108,7 +108,7 @@ services: CLUSTER_NODE_ID: "node-3" CLUSTER_HEARTBEAT_INTERVAL: "5s" CLUSTER_STALE_THRESHOLD: "15s" - CLUSTER_ENDPOINT: "http://switchboard-3:8080" + CLUSTER_ENDPOINT: "http://armature-3:8080" depends_on: postgres: condition: service_healthy @@ -122,6 +122,6 @@ services: ports: - "3000:80" depends_on: - - switchboard-1 - - switchboard-2 - - switchboard-3 + - armature-1 + - armature-2 + - armature-3 diff --git a/docker-compose-keycloak.yml b/docker-compose-keycloak.yml index 5c58a8a..82b1e69 100644 --- a/docker-compose-keycloak.yml +++ b/docker-compose-keycloak.yml @@ -6,11 +6,11 @@ # docker compose -f docker-compose.yml -f docker-compose-keycloak.yml up # # This extends the base docker-compose.yml to add: -# - Keycloak IdP with a pre-configured "switchboard" realm -# - Switchboard configured with AUTH_MODE=oidc pointing at Keycloak +# - Keycloak IdP with a pre-configured "armature" realm +# - Armature configured with AUTH_MODE=oidc pointing at Keycloak # # After startup: -# Switchboard: http://localhost:3000 +# Armature: http://localhost:3000 # Keycloak: http://localhost:8180 (admin / admin) # # Pre-configured test users in Keycloak: @@ -21,14 +21,14 @@ # ============================================ services: - # Override switchboard to use OIDC - switchboard: + # Override armature to use OIDC + armature: environment: AUTH_MODE: oidc - OIDC_ISSUER_URL: http://keycloak:8080/realms/switchboard - OIDC_EXTERNAL_ISSUER_URL: http://localhost:8180/realms/switchboard - OIDC_CLIENT_ID: switchboard - OIDC_CLIENT_SECRET: switchboard-secret + OIDC_ISSUER_URL: http://keycloak:8080/realms/armature + OIDC_EXTERNAL_ISSUER_URL: http://localhost:8180/realms/armature + OIDC_CLIENT_ID: armature + OIDC_CLIENT_SECRET: armature-secret OIDC_REDIRECT_URL: http://localhost:3000/api/v1/auth/oidc/callback OIDC_AUTO_ACTIVATE: "true" OIDC_ADMIN_ROLE: sb-admin @@ -38,7 +38,7 @@ services: keycloak: image: quay.io/keycloak/keycloak:26.0 - container_name: switchboard-keycloak + container_name: armature-keycloak command: - start-dev - --import-realm @@ -48,7 +48,7 @@ services: KC_HTTP_PORT: "8080" KC_HEALTH_ENABLED: "true" volumes: - - ./ci/keycloak-realm.json:/opt/keycloak/data/import/switchboard-realm.json:ro + - ./ci/keycloak-realm.json:/opt/keycloak/data/import/armature-realm.json:ro ports: - "8180:8080" healthcheck: diff --git a/docker-compose-upgrade.yml b/docker-compose-upgrade.yml index 963b4e8..f45adac 100644 --- a/docker-compose-upgrade.yml +++ b/docker-compose-upgrade.yml @@ -7,11 +7,11 @@ # ci/e2e-upgrade-test.sh (orchestrates build → seed → upgrade → verify) # # Manual usage: -# docker build -t switchboard-core:v-old . -# docker compose -f docker-compose-upgrade.yml up postgres switchboard-old -d +# docker build -t armature:v-old . +# docker compose -f docker-compose-upgrade.yml up postgres armature-old -d # # ... seed data ... -# docker compose -f docker-compose-upgrade.yml stop switchboard-old -# docker compose -f docker-compose-upgrade.yml up switchboard-new -d +# docker compose -f docker-compose-upgrade.yml stop armature-old +# docker compose -f docker-compose-upgrade.yml up armature-new -d # # ... verify data ... # docker compose -f docker-compose-upgrade.yml down -v @@ -19,28 +19,28 @@ services: postgres: image: postgres:16-alpine environment: - POSTGRES_DB: switchboard_upgrade - POSTGRES_USER: switchboard + POSTGRES_DB: armature_upgrade + POSTGRES_USER: armature POSTGRES_PASSWORD: upgrade-password ports: - "5433:5432" healthcheck: - test: ["CMD-SHELL", "pg_isready -U switchboard -d switchboard_upgrade"] + test: ["CMD-SHELL", "pg_isready -U armature -d armature_upgrade"] interval: 2s timeout: 5s retries: 10 - switchboard-old: - image: switchboard-core:v-old + armature-old: + image: armature:v-old environment: PORT: "8080" BASE_PATH: "" DB_DRIVER: postgres - DATABASE_URL: postgres://switchboard:upgrade-password@postgres:5432/switchboard_upgrade?sslmode=disable + DATABASE_URL: postgres://armature:upgrade-password@postgres:5432/armature_upgrade?sslmode=disable JWT_SECRET: upgrade-jwt-secret ENCRYPTION_KEY: upgrade-encryption-key-32chars!! - SWITCHBOARD_ADMIN_USERNAME: admin - SWITCHBOARD_ADMIN_PASSWORD: admin + ARMATURE_ADMIN_USERNAME: admin + ARMATURE_ADMIN_PASSWORD: admin STORAGE_BACKEND: pvc STORAGE_PATH: /data/storage CORS_ALLOWED_ORIGINS: "*" @@ -56,17 +56,17 @@ services: volumes: - upgrade_storage:/data/storage - switchboard-new: - image: core-switchboard-new + armature-new: + image: core-armature-new environment: PORT: "8080" BASE_PATH: "" DB_DRIVER: postgres - DATABASE_URL: postgres://switchboard:upgrade-password@postgres:5432/switchboard_upgrade?sslmode=disable + DATABASE_URL: postgres://armature:upgrade-password@postgres:5432/armature_upgrade?sslmode=disable JWT_SECRET: upgrade-jwt-secret ENCRYPTION_KEY: upgrade-encryption-key-32chars!! - SWITCHBOARD_ADMIN_USERNAME: admin - SWITCHBOARD_ADMIN_PASSWORD: admin + ARMATURE_ADMIN_USERNAME: admin + ARMATURE_ADMIN_PASSWORD: admin STORAGE_BACKEND: pvc STORAGE_PATH: /data/storage CORS_ALLOWED_ORIGINS: "*" diff --git a/docker-compose.yml b/docker-compose.yml index bb1419f..c67e6a7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,20 +13,20 @@ # For Postgres / multi-replica / k8s deployment see k8s/ and Dockerfile. services: - switchboard: + armature: build: context: . dockerfile: Dockerfile - container_name: switchboard-core + container_name: armature environment: PORT: "8080" BASE_PATH: "" DB_DRIVER: sqlite - DATABASE_URL: /data/switchboard.db + DATABASE_URL: /data/armature.db JWT_SECRET: ${JWT_SECRET:-change-me-for-production} ENCRYPTION_KEY: ${ENCRYPTION_KEY:-change-me-for-production} - SWITCHBOARD_ADMIN_USERNAME: ${ADMIN_USERNAME:-admin} - SWITCHBOARD_ADMIN_PASSWORD: ${ADMIN_PASSWORD:-admin} + ARMATURE_ADMIN_USERNAME: ${ADMIN_USERNAME:-admin} + ARMATURE_ADMIN_PASSWORD: ${ADMIN_PASSWORD:-admin} STORAGE_BACKEND: pvc STORAGE_PATH: /data/storage CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-http://localhost:3000} diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 473c10c..dc97029 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -1,6 +1,6 @@ #!/bin/bash # ============================================ -# Switchboard Core - Backend Launcher +# Armature - Backend Launcher # ============================================ # Runs as an nginx entrypoint.d hook. # Starts the Go backend in the background @@ -20,15 +20,15 @@ else -exec sed -i "s|%%BASE_PATH%%||g" {} + fi -echo "🔀 Starting Switchboard Core backend..." +echo "🔀 Starting Armature backend..." # Kill any stale backend from a previous entrypoint run -pkill -f /usr/local/bin/switchboard 2>/dev/null || true +pkill -f /usr/local/bin/armature 2>/dev/null || true sleep 0.2 # Launch Go backend in background (from /app so migrations are found) cd /app -/usr/local/bin/switchboard & +/usr/local/bin/armature & BACKEND_PID=$! # Wait for backend to be ready (max 60s) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 6a23353..950abf7 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1,6 +1,6 @@ -# Switchboard Core — Architecture +# Armature — Architecture -Switchboard Core is a self-hosted extension platform. It provides identity, +Armature is a self-hosted extension platform. It provides identity, teams, permissions, storage, workflows, notifications, and a package system. Everything else — chat, AI providers, personas, knowledge bases, notes, tools — ships as installable extensions. @@ -284,7 +284,7 @@ Single Docker image: Go binary + migrations + frontend assets + vendor libs. Kubernetes deployment with 3-node PG cluster. CI via Gitea Actions with DaemonSet DinD runners testing both PG and SQLite pipelines. -Registry: `registry.gobha.me:5000/xcaliber/switchboard-core` +Registry: `registry.gobha.me:5000/xcaliber/armature` Namespace: `gobha-ai-chat` ### Cluster Topology diff --git a/docs/DEMO-WORKFLOWS.md b/docs/DEMO-WORKFLOWS.md index ccaca6b..568985b 100644 --- a/docs/DEMO-WORKFLOWS.md +++ b/docs/DEMO-WORKFLOWS.md @@ -280,7 +280,7 @@ def on_fire(ctx): resp = http.post(url, body=payload, headers={ "Content-Type": "application/json", - "X-Switchboard-Event": data.get("event_name", "workflow.notify") + "X-Armature-Event": data.get("event_name", "workflow.notify") }) # Log the delivery diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 5405f03..8214612 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -3,14 +3,14 @@ ## Docker Single-Instance ```bash -docker pull ghcr.io/switchboard-core/switchboard-core:latest +docker pull ghcr.io/armature/armature:latest docker run -p 8080:80 \ - -e SWITCHBOARD_ADMIN_USERNAME=admin \ - -e SWITCHBOARD_ADMIN_PASSWORD=changeme \ + -e ARMATURE_ADMIN_USERNAME=admin \ + -e ARMATURE_ADMIN_PASSWORD=changeme \ -e JWT_SECRET="$(openssl rand -hex 32)" \ -e ENCRYPTION_KEY="$(openssl rand -hex 32)" \ - -v switchboard-data:/data \ - ghcr.io/switchboard-core/switchboard-core:latest + -v armature-data:/data \ + ghcr.io/armature/armature:latest ``` This runs with SQLite and PVC storage. Suitable for evaluation and small teams. @@ -22,22 +22,22 @@ services: postgres: image: postgres:16 environment: - POSTGRES_DB: switchboard - POSTGRES_USER: switchboard + POSTGRES_DB: armature + POSTGRES_USER: armature POSTGRES_PASSWORD: secretpassword volumes: - pg_data:/var/lib/postgresql/data - switchboard: - image: ghcr.io/switchboard-core/switchboard-core:latest + armature: + image: ghcr.io/armature/armature:latest ports: - "8080:80" environment: - DATABASE_URL: "postgres://switchboard:secretpassword@postgres:5432/switchboard?sslmode=disable" + DATABASE_URL: "postgres://armature:secretpassword@postgres:5432/armature?sslmode=disable" JWT_SECRET: "change-me-in-production" ENCRYPTION_KEY: "change-me-in-production" - SWITCHBOARD_ADMIN_USERNAME: admin - SWITCHBOARD_ADMIN_PASSWORD: changeme + ARMATURE_ADMIN_USERNAME: admin + ARMATURE_ADMIN_PASSWORD: changeme STORAGE_BACKEND: pvc STORAGE_PATH: /data/storage volumes: @@ -58,7 +58,7 @@ See the `k8s/` directory for example manifests. Key considerations: - Liveness probe: `/healthz/live`. Readiness probe: `/healthz/ready`. - Mount a PVC at `/data/storage` or configure S3. - Store `JWT_SECRET` and `ENCRYPTION_KEY` in Kubernetes Secrets. -- Registry: `registry.gobha.me:5000/xcaliber/switchboard-core`. +- Registry: `registry.gobha.me:5000/xcaliber/armature`. ## Environment Variables @@ -72,15 +72,15 @@ See the `k8s/` directory for example manifests. Key considerations: | `AUTH_MODE` | `builtin` | `builtin`, `mtls`, `oidc` | | `STORAGE_BACKEND` | auto | `pvc` or `s3` | | `STORAGE_PATH` | `/data/storage` | PVC mount point | -| `BASE_PATH` | | URL prefix (e.g., `/switchboard`) | +| `BASE_PATH` | | URL prefix (e.g., `/armature`) | | `LOG_FORMAT` | `text` | `text` or `json` | | `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error` | | `CORS_ALLOWED_ORIGINS` | | Comma-separated allowed origins | | `BUNDLED_PACKAGES` | (empty) | `""` defaults, `"*"` all, or comma-separated | | `SKIP_BUNDLED_PACKAGES` | `false` | Disable bundled package install | | `BUNDLED_PACKAGES_DIR` | `/app/bundled-packages` | Custom bundle directory | -| `SWITCHBOARD_ADMIN_USERNAME` | | Bootstrap admin username | -| `SWITCHBOARD_ADMIN_PASSWORD` | | Bootstrap admin password | +| `ARMATURE_ADMIN_USERNAME` | | Bootstrap admin username | +| `ARMATURE_ADMIN_PASSWORD` | | Bootstrap admin password | | `SEED_USERS` | | Dev seed users (ignored in production) | ### S3 Storage Variables @@ -107,14 +107,14 @@ See the `k8s/` directory for example manifests. Key considerations: **PostgreSQL** (recommended for production): ```bash -DATABASE_URL="postgres://user:pass@host:5432/switchboard?sslmode=require" +DATABASE_URL="postgres://user:pass@host:5432/armature?sslmode=require" ``` **SQLite** (dev, test, edge deployments): ```bash DB_DRIVER=sqlite -DATABASE_URL=/data/switchboard.db +DATABASE_URL=/data/armature.db ``` Both databases are first-class -- every query compiles and passes tests on both. The driver is auto-detected from `DATABASE_URL` if `DB_DRIVER` is not set. diff --git a/docs/DESIGN-cluster-registry.md b/docs/DESIGN-cluster-registry.md index 7f06302..85e3671 100644 --- a/docs/DESIGN-cluster-registry.md +++ b/docs/DESIGN-cluster-registry.md @@ -9,7 +9,7 @@ ## Problem -Switchboard-core is tightly coupled to PostgreSQL. Scaling horizontally requires instance coordination: peer discovery, health monitoring, ephemeral event routing, and (optionally) leader election. Traditional HA solutions (Raft, etcd, Consul) introduce a second consensus layer on top of PG — doubling operational complexity for a system that already has serializable transactions and LISTEN/NOTIFY. +Armature-core is tightly coupled to PostgreSQL. Scaling horizontally requires instance coordination: peer discovery, health monitoring, ephemeral event routing, and (optionally) leader election. Traditional HA solutions (Raft, etcd, Consul) introduce a second consensus layer on top of PG — doubling operational complexity for a system that already has serializable transactions and LISTEN/NOTIFY. ## Principle @@ -241,7 +241,7 @@ Pre-MVP: fold `CREATE UNLOGGED TABLE` into existing schema initialization. No ne ### Single-Node Behavior -When only one node is registered, the system behaves identically to pre-cluster switchboard-core. The registry has one row. LISTEN/NOTIFY delivers events back to the same instance. No special-casing required. +When only one node is registered, the system behaves identically to pre-cluster armature. The registry has one row. LISTEN/NOTIFY delivers events back to the same instance. No special-casing required. ### Health Endpoint Integration diff --git a/docs/DESIGN-native-mtls.md b/docs/DESIGN-native-mtls.md index febc932..8578998 100644 --- a/docs/DESIGN-native-mtls.md +++ b/docs/DESIGN-native-mtls.md @@ -172,17 +172,17 @@ their own `node-N.key`. ### Cert Provisioning Tooling -A shell script (`scripts/switchboard-ca.sh`) wrapping `openssl` is the +A shell script (`scripts/armature-ca.sh`) wrapping `openssl` is the KISS path. No new binary, no new dependency. Three commands: ``` -switchboard-ca init +armature-ca init → generates cluster-ca.crt + cluster-ca.key in ./ca/ -switchboard-ca issue-node --name node-1 --san "node-1.internal,10.0.0.1" +armature-ca issue-node --name node-1 --san "node-1.internal,10.0.0.1" → generates node-1.crt + node-1.key in ./nodes/ -switchboard-ca issue-user --cn jeff [--email jeff@example.com] +armature-ca issue-user --cn jeff [--email jeff@example.com] → generates jeff.crt + jeff.key in ./users/ ``` @@ -280,7 +280,7 @@ The new code is: | `server/auth/mtls_helpers.go` | Shared: `ParseDN`, `FingerprintCert`, `resolveOrProvision` | | `server/auth/mtls_native_test.go` | Unit + integration tests | | `server/config/tls.go` | `TLSConfig` struct, loader, validation | -| `scripts/switchboard-ca.sh` | Cert provisioning wrapper | +| `scripts/armature-ca.sh` | Cert provisioning wrapper | `server/auth/mtls.go` (existing) is renamed to `mtls_proxy.go` for clarity. No behavioral changes to the proxy provider. diff --git a/docs/DISTRIBUTION.md b/docs/DISTRIBUTION.md index 04f5337..41ac4ff 100644 --- a/docs/DISTRIBUTION.md +++ b/docs/DISTRIBUTION.md @@ -3,11 +3,11 @@ ## Quick Start ```bash -docker pull ghcr.io/switchboard-core/switchboard-core:latest +docker pull ghcr.io/armature/armature:latest docker run -p 8080:80 \ - -e SWITCHBOARD_ADMIN_USERNAME=admin \ - -e SWITCHBOARD_ADMIN_PASSWORD=changeme \ - ghcr.io/switchboard-core/switchboard-core:latest + -e ARMATURE_ADMIN_USERNAME=admin \ + -e ARMATURE_ADMIN_PASSWORD=changeme \ + ghcr.io/armature/armature:latest ``` On first run, bundled packages are automatically installed — workflows, surfaces, and extensions are ready to use immediately. @@ -66,12 +66,12 @@ Set `BUNDLED_PACKAGES` to control which packages are installed: # Install ALL packages (everything in the image) docker run -p 8080:80 \ -e BUNDLED_PACKAGES="*" \ - ghcr.io/switchboard-core/switchboard-core:latest + ghcr.io/armature/armature:latest # Install specific packages only docker run -p 8080:80 \ -e BUNDLED_PACKAGES="notes,tasks,schedules" \ - ghcr.io/switchboard-core/switchboard-core:latest + ghcr.io/armature/armature:latest ``` Empty (default) installs the curated default set. Use `*` to install all packages. This is useful for Helm charts where different environments need different packages. @@ -83,7 +83,7 @@ Set `SKIP_BUNDLED_PACKAGES=true` to prevent bundled packages from being installe ```bash docker run -p 8080:80 \ -e SKIP_BUNDLED_PACKAGES=true \ - ghcr.io/switchboard-core/switchboard-core:latest + ghcr.io/armature/armature:latest ``` ### Custom Bundle Directory @@ -94,7 +94,7 @@ Override the default bundled packages location with `BUNDLED_PACKAGES_DIR`: docker run -p 8080:80 \ -e BUNDLED_PACKAGES_DIR=/custom/packages \ -v /host/packages:/custom/packages \ - ghcr.io/switchboard-core/switchboard-core:latest + ghcr.io/armature/armature:latest ``` ## Builder Image @@ -102,7 +102,7 @@ docker run -p 8080:80 \ The builder image pre-caches Go modules and Node dependencies for faster custom builds. ```bash -docker pull ghcr.io/switchboard-core/builder:latest +docker pull ghcr.io/armature/builder:latest ``` ### What It Caches @@ -117,16 +117,16 @@ docker pull ghcr.io/switchboard-core/builder:latest Reference the builder image as a base stage in your Dockerfile: ```dockerfile -FROM ghcr.io/switchboard-core/builder:latest AS builder +FROM ghcr.io/armature/builder:latest AS builder WORKDIR /app COPY server/ . -RUN go build -ldflags="-s -w" -o /bin/switchboard . +RUN go build -ldflags="-s -w" -o /bin/armature . ``` ### Building Locally ```bash -docker build -f Dockerfile.builder -t switchboard-builder . +docker build -f Dockerfile.builder -t armature-builder . ``` ## Custom Build Guide @@ -135,7 +135,7 @@ docker build -f Dockerfile.builder -t switchboard-builder . 1. Create your package in `packages/your-package/` with a `manifest.json` 2. Build all packages: `cd packages && bash build.sh all` -3. Build the Docker image: `docker build -t my-switchboard .` +3. Build the Docker image: `docker build -t my-armature .` The Dockerfile automatically builds all packages in the `packages/` directory and bundles them into the production image. @@ -148,14 +148,14 @@ To exclude specific packages from the bundle, either: ### Forking for Custom Builds ```bash -git clone https://github.com/switchboard-core/switchboard-core.git -cd switchboard-core +git clone https://github.com/armature/armature.git +cd armature # Add/modify packages cp -r my-extension packages/my-extension/ # Build with builder image for faster compilation -docker build -t my-switchboard . +docker build -t my-armature . ``` ## Production Deployment @@ -172,7 +172,7 @@ docker build -t my-switchboard . | `AUTH_MODE` | `builtin` | `builtin`, `mtls`, or `oidc` | | `STORAGE_BACKEND` | (auto) | `pvc` or `s3` | | `STORAGE_PATH` | `/data/storage` | PVC mount point | -| `BASE_PATH` | | URL prefix (e.g. `/switchboard`) | +| `BASE_PATH` | | URL prefix (e.g. `/armature`) | | `SKIP_BUNDLED_PACKAGES` | `false` | Disable auto-install of bundled packages | | `BUNDLED_PACKAGES` | (empty = defaults) | `""` curated defaults, `"*"` all, or comma-separated IDs | | `BUNDLED_PACKAGES_DIR` | `/app/bundled-packages` | Override bundled packages location | @@ -186,16 +186,16 @@ PostgreSQL is recommended for production. SQLite is suitable for single-instance ```bash # PostgreSQL (recommended) docker run -p 8080:80 \ - -e DATABASE_URL="postgres://user:pass@host:5432/switchboard?sslmode=require" \ + -e DATABASE_URL="postgres://user:pass@host:5432/armature?sslmode=require" \ -e JWT_SECRET="$(openssl rand -hex 32)" \ -e ENCRYPTION_KEY="$(openssl rand -hex 32)" \ - ghcr.io/switchboard-core/switchboard-core:latest + ghcr.io/armature/armature:latest # SQLite (evaluation only) docker run -p 8080:80 \ -e DB_DRIVER=sqlite \ - -v switchboard-data:/data \ - ghcr.io/switchboard-core/switchboard-core:latest + -v armature-data:/data \ + ghcr.io/armature/armature:latest ``` ### Storage @@ -204,12 +204,12 @@ Object storage is required for file uploads and package asset extraction. ```bash # PVC (auto-detected if path is writable) -docker run -v switchboard-storage:/data/storage ... +docker run -v armature-storage:/data/storage ... # S3-compatible (MinIO, AWS S3, Ceph) docker run \ -e STORAGE_BACKEND=s3 \ - -e S3_BUCKET=switchboard \ + -e S3_BUCKET=armature \ -e S3_ENDPOINT=https://minio.corp:9000 \ -e S3_ACCESS_KEY=... \ -e S3_SECRET_KEY=... \ diff --git a/docs/GETTING-STARTED.md b/docs/GETTING-STARTED.md index c0f13d2..c3469eb 100644 --- a/docs/GETTING-STARTED.md +++ b/docs/GETTING-STARTED.md @@ -20,10 +20,10 @@ docker compose down -v ## First Boot -On first start, Switchboard Core will: +On first start, Armature will: 1. Run database migrations (SQLite by default in compose). -2. Create the admin user from `SWITCHBOARD_ADMIN_USERNAME` / `SWITCHBOARD_ADMIN_PASSWORD` env vars. +2. Create the admin user from `ARMATURE_ADMIN_USERNAME` / `ARMATURE_ADMIN_PASSWORD` env vars. 3. Auto-install the curated default package set (notes, chat-core, dashboard, workflow demos, etc.). No manual setup steps are required. @@ -67,8 +67,8 @@ You can also upload `.pkg` archives through the Admin > Packages page. | `AUTH_MODE` | `builtin` | `builtin`, `mtls`, or `oidc` | | `STORAGE_BACKEND` | auto | `pvc` or `s3` | | `STORAGE_PATH` | `/data/storage` | PVC mount point | -| `SWITCHBOARD_ADMIN_USERNAME` | | Bootstrap admin username | -| `SWITCHBOARD_ADMIN_PASSWORD` | | Bootstrap admin password | +| `ARMATURE_ADMIN_USERNAME` | | Bootstrap admin username | +| `ARMATURE_ADMIN_PASSWORD` | | Bootstrap admin password | | `BUNDLED_PACKAGES` | (empty) | `""` = curated defaults, `"*"` = all, or comma-separated IDs | | `SKIP_BUNDLED_PACKAGES` | `false` | Disable auto-install entirely | | `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error` | @@ -77,7 +77,7 @@ You can also upload `.pkg` archives through the Admin > Packages page. ## From Source ```bash -git clone && cd switchboard-core +git clone && cd armature cp server/.env.example server/.env # edit DB credentials cd server && go run . # Backend on http://localhost:8080 diff --git a/docs/PACKAGE-FORMAT.md b/docs/PACKAGE-FORMAT.md index 212c60d..82f5a1c 100644 --- a/docs/PACKAGE-FORMAT.md +++ b/docs/PACKAGE-FORMAT.md @@ -1,6 +1,6 @@ # Package Format -Switchboard packages are distributed as `.pkg` files -- ZIP archives with a standard internal structure. +Armature packages are distributed as `.pkg` files -- ZIP archives with a standard internal structure. ## ZIP Structure @@ -125,6 +125,6 @@ To bundle custom packages into a Docker image: 1. Place your package in `packages/your-package/` with a `manifest.json`. 2. Run `cd packages && bash build.sh all`. -3. Build the image: `docker build -t my-switchboard .` +3. Build the image: `docker build -t my-armature .` The Dockerfile builds all packages and copies them into the bundled packages directory. diff --git a/docs/PACKAGE-REGISTRY.md b/docs/PACKAGE-REGISTRY.md index ade0005..0175858 100644 --- a/docs/PACKAGE-REGISTRY.md +++ b/docs/PACKAGE-REGISTRY.md @@ -15,7 +15,7 @@ The registry is a static JSON file matching the `RegistryResponse` struct: "title": "Notes", "version": "0.8.0", "description": "Markdown notes with backlinks and graph view", - "author": "switchboard", + "author": "armature", "type": "extension", "tier": "core", "download_url": "https://cdn.example.com/pkg/notes.pkg", diff --git a/docs/TUTORIAL-FIRST-EXTENSION.md b/docs/TUTORIAL-FIRST-EXTENSION.md index 450ca5c..dc9b915 100644 --- a/docs/TUTORIAL-FIRST-EXTENSION.md +++ b/docs/TUTORIAL-FIRST-EXTENSION.md @@ -1,9 +1,9 @@ # Build Your First Browser Extension This tutorial walks through building a browser extension that renders custom -code blocks, modeled on the CSV Table Viewer that ships with Switchboard. +code blocks, modeled on the CSV Table Viewer that ships with Armature. -**Prerequisites:** A running Switchboard instance, a text editor, and `zip`. +**Prerequisites:** A running Armature instance, a text editor, and `zip`. ## Step 1: Create the Directory diff --git a/k8s/switchboard.yaml b/k8s/armature.yaml similarity index 79% rename from k8s/switchboard.yaml rename to k8s/armature.yaml index 094ebd6..1655b6c 100644 --- a/k8s/switchboard.yaml +++ b/k8s/armature.yaml @@ -1,14 +1,14 @@ -# k8s/switchboard.yaml +# k8s/armature.yaml # ============================================ -# Switchboard Core - Deployment +# Armature Core - Deployment # ============================================ # Secrets: -# switchboard-db-credentials - POSTGRES_USER, POSTGRES_PASSWORD -# switchboard-admin - username, password, email (optional) -# switchboard-encryption - ENCRYPTION_KEY (required for v0.9.4+) +# armature-db-credentials - POSTGRES_USER, POSTGRES_PASSWORD +# armature-admin - username, password, email (optional) +# armature-encryption - ENCRYPTION_KEY (required for v0.9.4+) # # PVC: -# switchboard-storage - Extraction scratch (always needed) +# armature-storage - Extraction scratch (always needed) # Requires ReadWriteMany (RWX) — see k8s/storage-pvc.yaml # With S3 backend, PVC is small (extraction status only, not blobs) # @@ -23,32 +23,32 @@ # S3_ACCESS_KEY - S3 access key # S3_SECRET_KEY - S3 secret key # -# Admin bootstrap: on every pod start, if SWITCHBOARD_ADMIN_* env vars +# Admin bootstrap: on every pod start, if ARMATURE_ADMIN_* env vars # are set, the backend upserts an admin user. Change the secret and # restart the pod to reset the admin password. # ============================================ apiVersion: apps/v1 kind: Deployment metadata: - name: switchboard${DEPLOY_SUFFIX} + name: armature${DEPLOY_SUFFIX} namespace: ${NAMESPACE} labels: - app: switchboard + app: armature env: ${ENVIRONMENT} spec: replicas: ${REPLICAS} selector: matchLabels: - app: switchboard + app: armature env: ${ENVIRONMENT} template: metadata: labels: - app: switchboard + app: armature env: ${ENVIRONMENT} spec: containers: - - name: switchboard + - name: armature image: ${IMAGE}:${IMAGE_TAG} imagePullPolicy: Always ports: @@ -72,43 +72,43 @@ spec: - name: POSTGRES_USER valueFrom: secretKeyRef: - name: switchboard-db-credentials + name: armature-db-credentials key: POSTGRES_USER - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: - name: switchboard-db-credentials + name: armature-db-credentials key: POSTGRES_PASSWORD - name: DATABASE_URL value: "postgres://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@$(POSTGRES_HOST):$(POSTGRES_PORT)/$(POSTGRES_DB)?sslmode=disable" - - name: SWITCHBOARD_ADMIN_USERNAME + - name: ARMATURE_ADMIN_USERNAME valueFrom: secretKeyRef: - name: switchboard-admin + name: armature-admin key: username optional: true - - name: SWITCHBOARD_ADMIN_PASSWORD + - name: ARMATURE_ADMIN_PASSWORD valueFrom: secretKeyRef: - name: switchboard-admin + name: armature-admin key: password optional: true - - name: SWITCHBOARD_ADMIN_EMAIL + - name: ARMATURE_ADMIN_EMAIL valueFrom: secretKeyRef: - name: switchboard-admin + name: armature-admin key: email optional: true - name: SEED_USERS valueFrom: secretKeyRef: - name: switchboard-seed-users + name: armature-seed-users key: users optional: true - name: ENCRYPTION_KEY valueFrom: secretKeyRef: - name: switchboard-encryption + name: armature-encryption key: ENCRYPTION_KEY optional: true # File storage (v0.12.0+) @@ -120,37 +120,37 @@ spec: - name: S3_ENDPOINT valueFrom: secretKeyRef: - name: switchboard-s3 + name: armature-s3 key: S3_ENDPOINT optional: true - name: S3_BUCKET valueFrom: secretKeyRef: - name: switchboard-s3 + name: armature-s3 key: S3_BUCKET optional: true - name: S3_ACCESS_KEY valueFrom: secretKeyRef: - name: switchboard-s3 + name: armature-s3 key: S3_ACCESS_KEY optional: true - name: S3_SECRET_KEY valueFrom: secretKeyRef: - name: switchboard-s3 + name: armature-s3 key: S3_SECRET_KEY optional: true - name: S3_REGION valueFrom: secretKeyRef: - name: switchboard-s3 + name: armature-s3 key: S3_REGION optional: true - name: S3_PREFIX valueFrom: secretKeyRef: - name: switchboard-s3 + name: armature-s3 key: S3_PREFIX optional: true - name: S3_FORCE_PATH_STYLE @@ -191,19 +191,19 @@ spec: volumes: - name: storage persistentVolumeClaim: - claimName: switchboard-storage${DEPLOY_SUFFIX} + claimName: armature-storage${DEPLOY_SUFFIX} --- apiVersion: v1 kind: Service metadata: - name: switchboard${DEPLOY_SUFFIX} + name: armature${DEPLOY_SUFFIX} namespace: ${NAMESPACE} labels: - app: switchboard + app: armature env: ${ENVIRONMENT} spec: selector: - app: switchboard + app: armature env: ${ENVIRONMENT} ports: - protocol: TCP diff --git a/k8s/ingress.yaml b/k8s/ingress.yaml index 1a353ba..0b8445e 100644 --- a/k8s/ingress.yaml +++ b/k8s/ingress.yaml @@ -1,11 +1,11 @@ # k8s/ingress.yaml # ============================================ -# Switchboard Core - Ingress +# Armature Core - Ingress # ============================================ # Path-based routing on a single domain: -# switchboard.DOMAIN/ → production -# switchboard.DOMAIN/test/ → test (main branch) -# switchboard.DOMAIN/dev/ → dev (PR branches) +# armature.DOMAIN/ → production +# armature.DOMAIN/test/ → test (main branch) +# armature.DOMAIN/dev/ → dev (PR branches) # # Each environment deploys its own Ingress resource. # Traefik merges rules for the same host automatically. @@ -18,29 +18,29 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: switchboard${DEPLOY_SUFFIX} + name: armature${DEPLOY_SUFFIX} namespace: ${NAMESPACE} annotations: cert-manager.io/cluster-issuer: "${CERT_ISSUER}" traefik.ingress.kubernetes.io/router.entrypoints: websecure labels: - app: switchboard + app: armature env: ${ENVIRONMENT} spec: ingressClassName: "traefik" tls: - hosts: - ${DEPLOY_HOST} - secretName: switchboard-tls + secretName: armature-tls rules: - host: "${DEPLOY_HOST}" http: paths: - # All traffic → switchboard (unified image) + # All traffic → armature (unified image) - path: ${BASE_PATH}/ pathType: Prefix backend: service: - name: switchboard${DEPLOY_SUFFIX} + name: armature${DEPLOY_SUFFIX} port: number: 80 diff --git a/k8s/middleware-retry.yaml b/k8s/middleware-retry.yaml index 42e245b..c2de0c2 100644 --- a/k8s/middleware-retry.yaml +++ b/k8s/middleware-retry.yaml @@ -1,6 +1,6 @@ # k8s/middleware-retry.yaml # ============================================ -# Switchboard Core - Traefik Retry Middleware +# Armature Core - Traefik Retry Middleware # ============================================ # Retries failed requests (502, connection reset) once before giving up. # Applies to API + WebSocket paths via the Ingress annotation. @@ -15,10 +15,10 @@ apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: - name: switchboard-retry${DEPLOY_SUFFIX} + name: armature-retry${DEPLOY_SUFFIX} namespace: ${NAMESPACE} labels: - app: switchboard + app: armature env: ${ENVIRONMENT} spec: retry: diff --git a/k8s/rbac-traefik.yaml b/k8s/rbac-traefik.yaml index 0f8b100..64c0450 100644 --- a/k8s/rbac-traefik.yaml +++ b/k8s/rbac-traefik.yaml @@ -16,7 +16,7 @@ metadata: name: traefik-middleware-manager namespace: ${NAMESPACE} labels: - app: switchboard + app: armature rules: - apiGroups: ["traefik.io"] resources: ["middlewares"] @@ -28,7 +28,7 @@ metadata: name: gitea-runner-traefik-middleware namespace: ${NAMESPACE} labels: - app: switchboard + app: armature subjects: - kind: ServiceAccount name: gitea-runner-sa diff --git a/k8s/storage-pvc.yaml b/k8s/storage-pvc.yaml index cd590c1..0819b6a 100644 --- a/k8s/storage-pvc.yaml +++ b/k8s/storage-pvc.yaml @@ -1,6 +1,6 @@ # k8s/storage-pvc.yaml # ============================================ -# Switchboard Core - Storage PVC +# Armature Core - Storage PVC # ============================================ # Persistent storage for file attachments, extraction queue, # and future knowledge base / compaction data. @@ -17,10 +17,10 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: switchboard-storage${DEPLOY_SUFFIX} + name: armature-storage${DEPLOY_SUFFIX} namespace: ${NAMESPACE} labels: - app: switchboard + app: armature component: storage env: ${ENVIRONMENT} spec: diff --git a/packages/README.md b/packages/README.md index f0140da..edf86f5 100644 --- a/packages/README.md +++ b/packages/README.md @@ -1,6 +1,6 @@ # Packages -Installable `.pkg` archives for Chat Switchboard. Each subdirectory is a +Installable `.pkg` archives for Chat Armature. Each subdirectory is a package — either a surface (routable UI), an extension (hooks/tools/pipes), or both (`full` type). diff --git a/packages/bug-report-triage/manifest.json b/packages/bug-report-triage/manifest.json index ff7948f..84a6f33 100644 --- a/packages/bug-report-triage/manifest.json +++ b/packages/bug-report-triage/manifest.json @@ -5,7 +5,7 @@ "version": "0.1.0", "tier": "browser", "icon": "🐛", - "author": "Switchboard Core", + "author": "Armature", "description": "Public bug report submission with severity-based routing, team assignment, and SLA timers.", "permissions": [], diff --git a/packages/chat-core/manifest.json b/packages/chat-core/manifest.json index 0b70e87..2996629 100644 --- a/packages/chat-core/manifest.json +++ b/packages/chat-core/manifest.json @@ -5,7 +5,7 @@ "tier": "starlark", "version": "0.2.0", "description": "Core chat library — conversations, messages, participants, read cursors. Usable by other packages via lib.require().", - "author": "switchboard", + "author": "armature", "permissions": ["db.write", "realtime.publish"], diff --git a/packages/chat/manifest.json b/packages/chat/manifest.json index fd81a56..9e69328 100644 --- a/packages/chat/manifest.json +++ b/packages/chat/manifest.json @@ -9,7 +9,7 @@ "version": "0.2.0", "icon": "\ud83d\udcac", "description": "Chat surface — conversations, messaging, typing indicators, read receipts.", - "author": "switchboard", + "author": "armature", "permissions": ["db.write", "realtime.publish"], diff --git a/packages/content-approval/manifest.json b/packages/content-approval/manifest.json index 24dcc67..d417f05 100644 --- a/packages/content-approval/manifest.json +++ b/packages/content-approval/manifest.json @@ -5,7 +5,7 @@ "version": "0.1.0", "tier": "browser", "icon": "📝", - "author": "Switchboard Core", + "author": "Armature", "description": "Multi-party content review with quorum signoff and revision cycle.", "permissions": [], diff --git a/packages/csv-table/manifest.json b/packages/csv-table/manifest.json index 2597cef..fde04ec 100644 --- a/packages/csv-table/manifest.json +++ b/packages/csv-table/manifest.json @@ -4,7 +4,7 @@ "version": "1.0.0", "type": "extension", "tier": "browser", - "author": "switchboard", + "author": "armature", "description": "Renders ```csv code blocks as sortable, interactive HTML tables", "requires": [], "permissions": [], diff --git a/packages/dashboard/js/main.js b/packages/dashboard/js/main.js index 861a773..2d8f835 100644 --- a/packages/dashboard/js/main.js +++ b/packages/dashboard/js/main.js @@ -1,5 +1,5 @@ // ========================================== -// Switchboard Core — Dashboard Package (v0.31.1) +// Armature — Dashboard Package (v0.31.1) // ========================================== // SDK Exercise Surface. Exercises every sw.* primitive with ZERO // component CSS overrides. All styling comes from the SDK itself. diff --git a/packages/dashboard/manifest.json b/packages/dashboard/manifest.json index 6fd9992..547f03a 100644 --- a/packages/dashboard/manifest.json +++ b/packages/dashboard/manifest.json @@ -4,7 +4,7 @@ "type": "full", "version": "0.31.1", "tier": "browser", - "author": "Switchboard Core", + "author": "Armature", "icon": "📊", "description": "Project dashboard exercising all SDK primitives (requires legacy sw.* SDK — dormant until rewritten)", "requires": ["legacy-sdk"], diff --git a/packages/diff-viewer/manifest.json b/packages/diff-viewer/manifest.json index e3b8f84..8716cf9 100644 --- a/packages/diff-viewer/manifest.json +++ b/packages/diff-viewer/manifest.json @@ -4,7 +4,7 @@ "version": "1.0.0", "type": "extension", "tier": "browser", - "author": "switchboard", + "author": "armature", "description": "Renders ```diff code blocks with syntax-highlighted additions, deletions, and context lines", "requires": [], "permissions": [], diff --git a/packages/editor/js/main.js b/packages/editor/js/main.js index 4e017e8..de948ee 100644 --- a/packages/editor/js/main.js +++ b/packages/editor/js/main.js @@ -1,5 +1,5 @@ // ========================================== -// Switchboard Core — Editor Package (v0.31.0) +// Armature — Editor Package (v0.31.0) // ========================================== // Installable .pkg that provides the code editor surface. // Mounts into #extension-mount (surface-extension template). diff --git a/packages/editor/manifest.json b/packages/editor/manifest.json index b9398dc..a641068 100644 --- a/packages/editor/manifest.json +++ b/packages/editor/manifest.json @@ -4,7 +4,7 @@ "type": "full", "version": "0.31.0", "tier": "browser", - "author": "Switchboard Core", + "author": "Armature", "icon": "✏️", "description": "Code editor with workspace management, file tree, and AI assist (requires legacy sw.* SDK — dormant until rewritten)", "requires": ["legacy-sdk"], diff --git a/packages/employee-onboarding/manifest.json b/packages/employee-onboarding/manifest.json index 428f72a..e068db8 100644 --- a/packages/employee-onboarding/manifest.json +++ b/packages/employee-onboarding/manifest.json @@ -5,7 +5,7 @@ "version": "0.1.0", "tier": "starlark", "icon": "🏢", - "author": "Switchboard Core", + "author": "Armature", "description": "Employee onboarding with automated provisioning, manager signoff, and welcome notification.", "permissions": ["db.write", "notifications.send"], diff --git a/packages/git-board/manifest.json b/packages/git-board/manifest.json index 8f05ddc..d855f2a 100644 --- a/packages/git-board/manifest.json +++ b/packages/git-board/manifest.json @@ -9,7 +9,7 @@ "version": "0.2.0", "icon": "🔀", "description": "Gitea issue and PR board powered by the gitea-client library. Uses extension connections for authentication.", - "author": "switchboard", + "author": "armature", "permissions": ["connections.read"], diff --git a/packages/gitea-client/manifest.json b/packages/gitea-client/manifest.json index b0fbc99..2710bb1 100644 --- a/packages/gitea-client/manifest.json +++ b/packages/gitea-client/manifest.json @@ -5,7 +5,7 @@ "tier": "starlark", "version": "1.0.1", "description": "Shared Gitea client — connections, API helpers, optional data caching.", - "author": "switchboard", + "author": "armature", "permissions": ["api.http", "connections.read", "db.read", "db.write"], diff --git a/packages/icd-test-runner/js/crud/dashboard-package.js b/packages/icd-test-runner/js/crud/dashboard-package.js index a7a93f4..2ff9d58 100644 --- a/packages/icd-test-runner/js/crud/dashboard-package.js +++ b/packages/icd-test-runner/js/crud/dashboard-package.js @@ -27,7 +27,7 @@ type: 'full', version: '0.31.1', tier: 'browser', - author: 'Switchboard Core', + author: 'Armature', description: 'Project dashboard exercising all SDK primitives', route: '/s/dashboard', permissions: [], diff --git a/packages/icd-test-runner/js/crud/editor-package.js b/packages/icd-test-runner/js/crud/editor-package.js index 10fd3ce..9d4bb88 100644 --- a/packages/icd-test-runner/js/crud/editor-package.js +++ b/packages/icd-test-runner/js/crud/editor-package.js @@ -27,7 +27,7 @@ type: 'full', version: '0.31.0', tier: 'browser', - author: 'Switchboard Core', + author: 'Armature', description: 'Code editor with workspace management', route: '/s/editor', layout: 'editor', diff --git a/packages/icd-test-runner/js/crud/observability.js b/packages/icd-test-runner/js/crud/observability.js index 7647586..3ce3880 100644 --- a/packages/icd-test-runner/js/crud/observability.js +++ b/packages/icd-test-runner/js/crud/observability.js @@ -14,9 +14,9 @@ var resp = await fetch(T.base + '/metrics'); T.assert(resp.ok, 'expected 200 from /metrics, got ' + resp.status); var text = await resp.text(); - T.assert(text.indexOf('switchboard_http_requests_total') !== -1, 'expected switchboard_http_requests_total in /metrics'); - T.assert(text.indexOf('switchboard_http_request_duration_seconds') !== -1, 'expected switchboard_http_request_duration_seconds in /metrics'); - T.assert(text.indexOf('switchboard_websocket_connections') !== -1, 'expected switchboard_websocket_connections in /metrics'); + T.assert(text.indexOf('armature_http_requests_total') !== -1, 'expected armature_http_requests_total in /metrics'); + T.assert(text.indexOf('armature_http_request_duration_seconds') !== -1, 'expected armature_http_request_duration_seconds in /metrics'); + T.assert(text.indexOf('armature_websocket_connections') !== -1, 'expected armature_websocket_connections in /metrics'); }); // -- GET /api/docs (Swagger UI) -- diff --git a/packages/icd-test-runner/js/main.js b/packages/icd-test-runner/js/main.js index 3bfe7b3..b636b2c 100644 --- a/packages/icd-test-runner/js/main.js +++ b/packages/icd-test-runner/js/main.js @@ -15,7 +15,7 @@ * 8. tier-security.js — Adversarial red-team tests (auth, cross-tenant, input validation) * 9. tier-providers.js — Three-tier provider CRUD + live completions * 10. tier-packaging.js — Extension permission lifecycle + secrets (v0.29.0) - * 11. tier-sdk.js — Switchboard SDK contract tests + * 11. tier-sdk.js — Armature SDK contract tests * 12. ui.js — Render functions, export, provider setup panel * 11. (this file) — Boot */ diff --git a/packages/icd-test-runner/js/tier-sdk.js b/packages/icd-test-runner/js/tier-sdk.js index d3c5cca..4fbdcff 100644 --- a/packages/icd-test-runner/js/tier-sdk.js +++ b/packages/icd-test-runner/js/tier-sdk.js @@ -3,7 +3,7 @@ * Validates Preact SDK (sw/sdk/index.js) contract: boot, identity, * REST client, events, theme, pipe registration, execution ordering, * scoping, halt semantics, error isolation, introspection. - * v0.37.14: Updated from Switchboard.init() to boot() API. + * v0.37.14: Updated from Armature.init() to boot() API. */ (function () { 'use strict'; diff --git a/packages/icd-test-runner/js/ui.js b/packages/icd-test-runner/js/ui.js index 460dfee..757fd24 100644 --- a/packages/icd-test-runner/js/ui.js +++ b/packages/icd-test-runner/js/ui.js @@ -236,7 +236,7 @@ }, 'Providers'); T.el.controls.appendChild(btnProv); - // SDK tier button — tests switchboard-sdk.js contract + // SDK tier button — tests armature-sdk.js contract var btnSdk = $('button', { className: (typeof window.sw !== 'undefined') ? 'btn-secondary' : 'btn-ghost', style: { opacity: (typeof window.sw !== 'undefined') ? '1' : '0.5' }, @@ -368,7 +368,7 @@ var totalMs = T.results.reduce(function (s, r) { return s + r.duration; }, 0); var lines = []; - lines.push('=== Switchboard Core ICD Test Report ==='); + lines.push('=== Armature ICD Test Report ==='); lines.push('Generated: ' + now); lines.push('Platform: v' + (T.manifest.version || '0.28.0')); lines.push('User: ' + T.user.username + ' (' + T.user.role + ')'); diff --git a/packages/js-sandbox/manifest.json b/packages/js-sandbox/manifest.json index fd81c5b..672501b 100644 --- a/packages/js-sandbox/manifest.json +++ b/packages/js-sandbox/manifest.json @@ -4,7 +4,7 @@ "version": "1.0.0", "type": "extension", "tier": "browser", - "author": "switchboard", + "author": "armature", "description": "Executes JavaScript in a sandboxed iframe. Allows the LLM to run code, verify calculations, and transform data — no server compute required.", "requires": ["chat"], "permissions": [], diff --git a/packages/katex-renderer/manifest.json b/packages/katex-renderer/manifest.json index 02c926d..7b20160 100644 --- a/packages/katex-renderer/manifest.json +++ b/packages/katex-renderer/manifest.json @@ -4,7 +4,7 @@ "version": "1.0.0", "type": "extension", "tier": "browser", - "author": "switchboard", + "author": "armature", "description": "Renders LaTeX math expressions: ```latex blocks and inline $...$ / $$...$$ syntax", "requires": [], "permissions": [], diff --git a/packages/mermaid-renderer/manifest.json b/packages/mermaid-renderer/manifest.json index 15b8d83..9176719 100644 --- a/packages/mermaid-renderer/manifest.json +++ b/packages/mermaid-renderer/manifest.json @@ -4,7 +4,7 @@ "version": "2.0.0", "type": "extension", "tier": "browser", - "author": "switchboard", + "author": "armature", "description": "Renders ```mermaid code blocks as interactive SVG diagrams with zoom/pan, SVG/PNG export, and source copy", "requires": [], "permissions": [], diff --git a/packages/notes/manifest.json b/packages/notes/manifest.json index 2157820..0626533 100644 --- a/packages/notes/manifest.json +++ b/packages/notes/manifest.json @@ -9,7 +9,7 @@ "version": "0.8.0", "icon": "📝", "description": "Markdown notes surface with rich editor, folders, tags, backlinks, import/export, sidebar tabs, document outline, and note graph.", - "author": "switchboard", + "author": "armature", "permissions": ["db.write"], diff --git a/packages/regex-tester/manifest.json b/packages/regex-tester/manifest.json index 88834c2..c85ce28 100644 --- a/packages/regex-tester/manifest.json +++ b/packages/regex-tester/manifest.json @@ -4,7 +4,7 @@ "version": "1.0.0", "type": "extension", "tier": "browser", - "author": "switchboard", + "author": "armature", "description": "Tests regular expressions against input strings. Allows the LLM to verify regex patterns and see matches, groups, and indices.", "requires": ["chat"], "permissions": [], diff --git a/packages/schedules/manifest.json b/packages/schedules/manifest.json index 73da3ac..b150a0e 100644 --- a/packages/schedules/manifest.json +++ b/packages/schedules/manifest.json @@ -4,7 +4,7 @@ "type": "full", "version": "0.1.0", "tier": "browser", - "author": "Switchboard Core", + "author": "Armature", "icon": "⏰", "description": "Manage cron jobs and scheduled automations", "route": "/s/schedules", diff --git a/packages/tasks/manifest.json b/packages/tasks/manifest.json index 674a427..af1396f 100644 --- a/packages/tasks/manifest.json +++ b/packages/tasks/manifest.json @@ -9,7 +9,7 @@ "version": "0.1.0", "icon": "✅", "description": "Task management surface with lifecycle events, webhook integration, and scheduled reminders.", - "author": "switchboard", + "author": "armature", "permissions": ["db.write", "notifications.send"], diff --git a/packages/team-activity-log/manifest.json b/packages/team-activity-log/manifest.json index b68a799..96f1f8f 100644 --- a/packages/team-activity-log/manifest.json +++ b/packages/team-activity-log/manifest.json @@ -9,7 +9,7 @@ "layout": "single", "version": "0.1.0", "description": "Example surface demonstrating Starlark API routes, extension-owned DB tables, admin settings, and SDK integration.", - "author": "switchboard", + "author": "armature", "permissions": ["api.http", "db.read", "db.write"], diff --git a/packages/webhook-notifier/manifest.json b/packages/webhook-notifier/manifest.json index 3edc3a0..fc7818c 100644 --- a/packages/webhook-notifier/manifest.json +++ b/packages/webhook-notifier/manifest.json @@ -5,7 +5,7 @@ "version": "0.1.0", "tier": "starlark", "icon": "🔔", - "author": "Switchboard Core", + "author": "Armature", "description": "Outbound webhook delivery with connection credential support and delivery logging.", "permissions": ["api.http", "connections.read", "db.write"], diff --git a/packages/workflow-chat/manifest.json b/packages/workflow-chat/manifest.json index 688dc10..6a4bae0 100644 --- a/packages/workflow-chat/manifest.json +++ b/packages/workflow-chat/manifest.json @@ -5,7 +5,7 @@ "tier": "starlark", "version": "0.1.0", "description": "Creates scoped chat conversations when workflow stages require team collaboration. Wire into stage_config as an on_advance hook.", - "author": "switchboard", + "author": "armature", "permissions": ["db.write", "realtime.publish"], diff --git a/packages/workflow-demo/js/main.js b/packages/workflow-demo/js/main.js index 090d7c0..2a020f6 100644 --- a/packages/workflow-demo/js/main.js +++ b/packages/workflow-demo/js/main.js @@ -1,5 +1,5 @@ // ========================================== -// Switchboard Core — Workflow Demo Surface +// Armature — Workflow Demo Surface // ========================================== // Interactive walkthrough of the four example workflow packages. // Shows workflow cards with feature badges, stage flow diagrams, diff --git a/packages/workflow-demo/manifest.json b/packages/workflow-demo/manifest.json index 70ca55c..d78f937 100644 --- a/packages/workflow-demo/manifest.json +++ b/packages/workflow-demo/manifest.json @@ -5,7 +5,7 @@ "version": "0.1.0", "tier": "browser", "icon": "🎓", - "author": "Switchboard Core", + "author": "Armature", "description": "Interactive demo surface for example workflow packages. Cards, stage diagrams, and API walkthroughs.", "route": "/s/workflow-demo", "auth": "authenticated", diff --git a/scripts/switchboard-ca.sh b/scripts/armature-ca.sh similarity index 95% rename from scripts/switchboard-ca.sh rename to scripts/armature-ca.sh index a361f66..1b8e4c3 100755 --- a/scripts/switchboard-ca.sh +++ b/scripts/armature-ca.sh @@ -1,14 +1,14 @@ #!/usr/bin/env bash # -# switchboard-ca.sh — Certificate provisioning for Switchboard Core mTLS. +# armature-ca.sh — Certificate provisioning for Armature mTLS. # # Wraps openssl to generate a cluster CA, node certificates (ServerAuth + # ClientAuth), and user certificates (ClientAuth only). All output is PEM. # # Usage: -# switchboard-ca init -# switchboard-ca issue-node --name --san -# switchboard-ca issue-user --cn [--email ] +# armature-ca init +# armature-ca issue-node --name --san +# armature-ca issue-user --cn [--email ] # # Files are written to the current directory under ca/, nodes/, users/. @@ -52,7 +52,7 @@ cmd_init() { -key "$CA_DIR/cluster-ca.key" \ -out "$CA_DIR/cluster-ca.crt" \ -days "$CA_DAYS" \ - -subj "/CN=Switchboard Cluster CA" \ + -subj "/CN=Armature Cluster CA" \ -addext "basicConstraints=critical,CA:TRUE" \ -addext "keyUsage=critical,keyCertSign,cRLSign" \ 2>/dev/null diff --git a/scripts/db-bootstrap.sh b/scripts/db-bootstrap.sh index b00fca8..5a7ce61 100644 --- a/scripts/db-bootstrap.sh +++ b/scripts/db-bootstrap.sh @@ -1,6 +1,6 @@ #!/bin/bash # ============================================ -# Switchboard Core - Database Bootstrap +# Armature - Database Bootstrap # ============================================ # Idempotent: safe to run on every CI build. # Creates the app role and database if they diff --git a/scripts/db-migrate.sh b/scripts/db-migrate.sh index 13efcdb..a533267 100644 --- a/scripts/db-migrate.sh +++ b/scripts/db-migrate.sh @@ -1,6 +1,6 @@ #!/bin/bash # ============================================ -# Switchboard Core - Database Migration Runner +# Armature - Database Migration Runner # ============================================ # NOTE: The Go backend auto-migrates on startup. # This script is for manual/emergency use only. diff --git a/scripts/db-validate.sh b/scripts/db-validate.sh index 68988b0..bad912a 100644 --- a/scripts/db-validate.sh +++ b/scripts/db-validate.sh @@ -1,6 +1,6 @@ #!/bin/bash # ============================================ -# Switchboard Core - Schema Validation (v0.16) +# Armature - Schema Validation (v0.16) # ============================================ # Verifies the database schema is correct after # migration. Checks expected tables, key columns, diff --git a/server/.env.example b/server/.env.example index a2a4188..5d90f40 100644 --- a/server/.env.example +++ b/server/.env.example @@ -1,4 +1,4 @@ -# Switchboard Core - Server Environment Variables +# Armature - Server Environment Variables # Copy this file to .env and fill in the values # ── Server ─────────────────────────────────── @@ -9,9 +9,9 @@ BASE_PATH= # e.g. /chat for path-based routing # ── Database (PostgreSQL) ──────────────────── DB_HOST=localhost DB_PORT=5432 -DB_USER=switchboard_core +DB_USER=armature DB_PASSWORD=your-secure-password-here -DB_NAME=switchboard_core +DB_NAME=armature DB_SSL_MODE=disable DB_MAX_CONNS=25 @@ -20,19 +20,19 @@ JWT_SECRET=your-super-secret-jwt-key-change-in-production # JWT durations (hardcoded in handlers/auth.go — not configurable via env): # Access token: 15 minutes # Refresh token: 7 days -JWT_ISSUER=switchboard-core +JWT_ISSUER=armature # ── Bootstrap Admin ────────────────────────── # Creates or updates admin account on every startup. # Set via K8s secret or env. Leave blank to skip. -SWITCHBOARD_ADMIN_USERNAME= -SWITCHBOARD_ADMIN_PASSWORD= +ARMATURE_ADMIN_USERNAME= +ARMATURE_ADMIN_PASSWORD= # ── Seed Users (dev/test only) ────────────── # Pre-create users on startup. Ignored in production. # Format: user:pass:role,user2:pass2:role2 (role = admin|user, default: user) # Idempotent: existing usernames are skipped. -# K8s: create secret "switchboard-seed-users" with key "users" +# K8s: create secret "armature-seed-users" with key "users" SEED_USERS= # ── CORS ───────────────────────────────────── @@ -57,7 +57,7 @@ STORAGE_PATH=/data/storage # S3 settings (only when STORAGE_BACKEND=s3) # Works with MinIO, Ceph RGW, AWS S3. # S3_ENDPOINT=http://minio:9000 -# S3_BUCKET=switchboard-storage +# S3_BUCKET=armature-storage # S3_ACCESS_KEY= # S3_SECRET_KEY= # S3_REGION=us-east-1 diff --git a/server/auth/auth.go b/server/auth/auth.go index 999aa74..04e51ea 100644 --- a/server/auth/auth.go +++ b/server/auth/auth.go @@ -5,8 +5,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/models" - "switchboard-core/store" + "armature/models" + "armature/store" ) type Mode string diff --git a/server/auth/builtin.go b/server/auth/builtin.go index a5ae52a..b5193f7 100644 --- a/server/auth/builtin.go +++ b/server/auth/builtin.go @@ -9,8 +9,8 @@ import ( "github.com/gin-gonic/gin" "golang.org/x/crypto/bcrypt" - "switchboard-core/models" - "switchboard-core/store" + "armature/models" + "armature/store" ) const bcryptCost = 12 diff --git a/server/auth/mock_idp_test.go b/server/auth/mock_idp_test.go index 75ae57c..33b8616 100644 --- a/server/auth/mock_idp_test.go +++ b/server/auth/mock_idp_test.go @@ -109,7 +109,7 @@ func (m *mockIdP) issueToken(sub, email, name string, groups []string, roles []s claims := jwt.MapClaims{ "iss": m.server.URL, "sub": sub, - "aud": "switchboard", + "aud": "armature", "exp": time.Now().Add(1 * time.Hour).Unix(), "iat": time.Now().Unix(), "email": email, @@ -140,7 +140,7 @@ func (m *mockIdP) issueExpiredToken(sub string) string { claims := jwt.MapClaims{ "iss": m.server.URL, "sub": sub, - "aud": "switchboard", + "aud": "armature", "exp": time.Now().Add(-1 * time.Hour).Unix(), "iat": time.Now().Add(-2 * time.Hour).Unix(), } @@ -160,7 +160,7 @@ func (m *mockIdP) issueTokenBadIssuer(sub string) string { claims := jwt.MapClaims{ "iss": "https://evil.example.com", "sub": sub, - "aud": "switchboard", + "aud": "armature", "exp": time.Now().Add(1 * time.Hour).Unix(), "iat": time.Now().Unix(), } diff --git a/server/auth/mtls_helpers.go b/server/auth/mtls_helpers.go index 505c9a7..5003b62 100644 --- a/server/auth/mtls_helpers.go +++ b/server/auth/mtls_helpers.go @@ -9,8 +9,8 @@ import ( "log" "strings" - "switchboard-core/models" - "switchboard-core/store" + "armature/models" + "armature/store" ) // ParseDN parses an RFC 2253 / RFC 4514 distinguished name into key-value pairs. diff --git a/server/auth/mtls_native.go b/server/auth/mtls_native.go index c7b8128..d5bb80f 100644 --- a/server/auth/mtls_native.go +++ b/server/auth/mtls_native.go @@ -6,7 +6,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/store" + "armature/store" ) // ErrNoCert is returned when no client certificate is presented on a diff --git a/server/auth/mtls_native_test.go b/server/auth/mtls_native_test.go index 9b5ea70..339af67 100644 --- a/server/auth/mtls_native_test.go +++ b/server/auth/mtls_native_test.go @@ -19,7 +19,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/store" + "armature/store" ) // ── Test CA helpers ────────────────────────────────────────────────── diff --git a/server/auth/mtls_proxy.go b/server/auth/mtls_proxy.go index e5a1510..06e41a9 100644 --- a/server/auth/mtls_proxy.go +++ b/server/auth/mtls_proxy.go @@ -5,7 +5,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/store" + "armature/store" ) // MTLSProxyConfig holds configuration for the proxy-terminated mTLS provider. diff --git a/server/auth/mtls_proxy_test.go b/server/auth/mtls_proxy_test.go index 7fc38ac..b93bbbe 100644 --- a/server/auth/mtls_proxy_test.go +++ b/server/auth/mtls_proxy_test.go @@ -3,7 +3,7 @@ package auth import ( "testing" - "switchboard-core/store" + "armature/store" ) func TestParseDN(t *testing.T) { diff --git a/server/auth/oidc.go b/server/auth/oidc.go index 8d519b3..78a675b 100644 --- a/server/auth/oidc.go +++ b/server/auth/oidc.go @@ -18,17 +18,17 @@ import ( "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" - "switchboard-core/models" - "switchboard-core/store" + "armature/models" + "armature/store" ) // OIDCConfig holds OIDC-specific configuration from env vars. type OIDCConfig struct { - IssuerURL string // e.g. "https://keycloak.corp/realms/switchboard" + IssuerURL string // e.g. "https://keycloak.corp/realms/armature" ExternalIssuerURL string // browser-facing URL (optional, defaults to IssuerURL) ClientID string ClientSecret string - RedirectURL string // e.g. "https://switchboard.corp/api/v1/auth/oidc/callback" + RedirectURL string // e.g. "https://armature.corp/api/v1/auth/oidc/callback" AutoActivate bool // auto-activate new users (default true) DefaultTeam string // team ID for auto-provisioned users (optional) RolesClaim string // JWT claim for role mapping (default "realm_access.roles") @@ -435,7 +435,7 @@ func (p *OIDCProvider) extractGroups(claims *oidcClaims) []string { // Keycloak puts groups in different places depending on mapper config. // Try the configured claim path as a fallback. // The claims.Groups field handles the simple case. This handles - // nested paths like "resource_access.switchboard.roles". + // nested paths like "resource_access.armature.roles". // For now, return empty — the direct Groups field covers the // standard Keycloak group mapper output. return nil diff --git a/server/auth/oidc_test.go b/server/auth/oidc_test.go index 4a2df3b..628ecac 100644 --- a/server/auth/oidc_test.go +++ b/server/auth/oidc_test.go @@ -3,7 +3,7 @@ package auth_test import ( "testing" - "switchboard-core/auth" + "armature/auth" ) func TestOIDCProvider_Discovery(t *testing.T) { @@ -12,7 +12,7 @@ func TestOIDCProvider_Discovery(t *testing.T) { p, err := auth.NewOIDCProvider(auth.OIDCConfig{ IssuerURL: idp.IssuerURL(), - ClientID: "switchboard", + ClientID: "armature", ClientSecret: "secret", AutoActivate: true, }) @@ -38,7 +38,7 @@ func TestOIDCProvider_Mode(t *testing.T) { p, err := auth.NewOIDCProvider(auth.OIDCConfig{ IssuerURL: idp.IssuerURL(), - ClientID: "switchboard", + ClientID: "armature", }) if err != nil { t.Fatalf("NewOIDCProvider: %v", err) @@ -55,7 +55,7 @@ func TestOIDCProvider_NoRegistration(t *testing.T) { p, err := auth.NewOIDCProvider(auth.OIDCConfig{ IssuerURL: idp.IssuerURL(), - ClientID: "switchboard", + ClientID: "armature", }) if err != nil { t.Fatalf("NewOIDCProvider: %v", err) @@ -68,7 +68,7 @@ func TestOIDCProvider_NoRegistration(t *testing.T) { func TestOIDCProvider_MissingIssuer(t *testing.T) { _, err := auth.NewOIDCProvider(auth.OIDCConfig{ - ClientID: "switchboard", + ClientID: "armature", }) if err == nil { t.Error("expected error for missing issuer URL") @@ -93,7 +93,7 @@ func TestOIDCProvider_AuthorizationURL(t *testing.T) { p, err := auth.NewOIDCProvider(auth.OIDCConfig{ IssuerURL: idp.IssuerURL(), - ClientID: "switchboard", + ClientID: "armature", }) if err != nil { t.Fatalf("NewOIDCProvider: %v", err) @@ -105,7 +105,7 @@ func TestOIDCProvider_AuthorizationURL(t *testing.T) { } // Should contain the required OIDC parameters - for _, want := range []string{"response_type=code", "client_id=switchboard", "state=test-state", "nonce=test-nonce"} { + for _, want := range []string{"response_type=code", "client_id=armature", "state=test-state", "nonce=test-nonce"} { if !containsStr(url, want) { t.Errorf("AuthorizationURL missing %q in %q", want, url) } @@ -115,7 +115,7 @@ func TestOIDCProvider_AuthorizationURL(t *testing.T) { func TestOIDCProvider_UnreachableIssuer(t *testing.T) { _, err := auth.NewOIDCProvider(auth.OIDCConfig{ IssuerURL: "http://127.0.0.1:1/unreachable", - ClientID: "switchboard", + ClientID: "armature", }) if err == nil { t.Error("expected error for unreachable issuer") diff --git a/server/auth/permissions.go b/server/auth/permissions.go index 878a430..fb9873a 100644 --- a/server/auth/permissions.go +++ b/server/auth/permissions.go @@ -5,8 +5,8 @@ import ( "encoding/json" "log" - "switchboard-core/database" - "switchboard-core/store" + "armature/database" + "armature/store" ) // EveryoneGroupID is the stable ID of the implicit "Everyone" group seeded in diff --git a/server/cluster/registry.go b/server/cluster/registry.go index 453cb23..a758770 100644 --- a/server/cluster/registry.go +++ b/server/cluster/registry.go @@ -9,7 +9,7 @@ import ( "sync" "time" - "switchboard-core/store" + "armature/store" ) // ConnCounter provides WebSocket connection count (satisfied by events.Hub). diff --git a/server/cluster/registry_test.go b/server/cluster/registry_test.go index 8f41ec0..eb36ad3 100644 --- a/server/cluster/registry_test.go +++ b/server/cluster/registry_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "switchboard-core/store" + "armature/store" ) // mockClusterStore implements store.ClusterStore for unit tests. diff --git a/server/config/config.go b/server/config/config.go index c3cc855..4fef293 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -44,7 +44,7 @@ type Config struct { S3Region string // AWS region (default "us-east-1") S3AccessKey string // access key ID S3SecretKey string // secret access key - S3Prefix string // optional key prefix within bucket (e.g. "switchboard/") + S3Prefix string // optional key prefix within bucket (e.g. "armature/") S3ForcePathStyle bool // use path-style URLs (required for MinIO, most self-hosted) // Structured logging @@ -85,11 +85,11 @@ type Config struct { BundledPackages string // OIDC - OIDCIssuerURL string // e.g. "https://keycloak.corp/realms/switchboard" + OIDCIssuerURL string // e.g. "https://keycloak.corp/realms/armature" OIDCExternalIssuerURL string // OIDC_EXTERNAL_ISSUER_URL OIDCClientID string OIDCClientSecret string - OIDCRedirectURL string // e.g. "https://switchboard.corp/api/v1/auth/oidc/callback" + OIDCRedirectURL string // e.g. "https://armature.corp/api/v1/auth/oidc/callback" OIDCAutoActivate bool // auto-activate new users (default true) OIDCDefaultTeam string // team ID for auto-provisioned users (optional) OIDCRolesClaim string // JWT claim for role mapping (default "realm_access.roles") @@ -127,9 +127,9 @@ func Load() *Config { JWTSecret: getEnv("JWT_SECRET", "dev-secret-change-me"), Environment: getEnv("ENVIRONMENT", "development"), BasePath: sanitizeBasePath(getEnv("BASE_PATH", "")), - AdminUsername: getEnv("SWITCHBOARD_ADMIN_USERNAME", ""), - AdminPassword: getEnv("SWITCHBOARD_ADMIN_PASSWORD", ""), - AdminEmail: getEnv("SWITCHBOARD_ADMIN_EMAIL", ""), + AdminUsername: getEnv("ARMATURE_ADMIN_USERNAME", ""), + AdminPassword: getEnv("ARMATURE_ADMIN_PASSWORD", ""), + AdminEmail: getEnv("ARMATURE_ADMIN_EMAIL", ""), SeedUsers: getEnv("SEED_USERS", ""), EncryptionKey: getEnv("ENCRYPTION_KEY", ""), @@ -207,7 +207,7 @@ func resolveDatabaseURL() string { port := getEnv("POSTGRES_PORT", "5432") user := getEnv("POSTGRES_USER", "") pass := getEnv("POSTGRES_PASSWORD", "") - db := getEnv("POSTGRES_DB", "switchboard_core") + db := getEnv("POSTGRES_DB", "armature") ssl := getEnv("POSTGRES_SSLMODE", "disable") return fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=%s", user, pass, host, port, db, ssl) } diff --git a/server/crypto/rekey.go b/server/crypto/rekey.go index 9e75aa8..1d4eaa6 100644 --- a/server/crypto/rekey.go +++ b/server/crypto/rekey.go @@ -15,7 +15,7 @@ import ( // // Usage: // -// switchboard vault rekey +// armature vault rekey // ENCRYPTION_KEY = current/old key // NEW_ENCRYPTION_KEY = new key to re-encrypt with func Rekey(db *sql.DB, oldEnvKey, newEnvKey string) error { diff --git a/server/crypto/status.go b/server/crypto/status.go index 9211bdf..dddc7e6 100644 --- a/server/crypto/status.go +++ b/server/crypto/status.go @@ -13,7 +13,7 @@ type VaultStatusInfo struct { } // VaultStatus gathers vault health metrics from the database. -// Used by both the CLI (`switchboard vault status`) and the admin API endpoint. +// Used by both the CLI (`armature vault status`) and the admin API endpoint. func VaultStatus(db *sql.DB, encryptionKey string) (*VaultStatusInfo, error) { if db == nil { return nil, fmt.Errorf("database not available") diff --git a/server/crypto/vault.go b/server/crypto/vault.go index 9d4da6b..c9bac34 100644 --- a/server/crypto/vault.go +++ b/server/crypto/vault.go @@ -1,4 +1,4 @@ -// Package crypto provides API key encryption primitives for Switchboard Core. +// Package crypto provides API key encryption primitives for Armature. // // Two-tier model: // @@ -59,7 +59,7 @@ func DeriveKeyFromEnv(envValue string) ([]byte, error) { } // HKDF with SHA-256: extract + expand with context string for domain separation. // No salt (nil) — the env value itself is the input keying material. - hkdfReader := hkdf.New(sha256.New, []byte(envValue), nil, []byte("switchboard-api-key-encryption-v1")) + hkdfReader := hkdf.New(sha256.New, []byte(envValue), nil, []byte("armature-api-key-encryption-v1")) key := make([]byte, 32) if _, err := io.ReadFull(hkdfReader, key); err != nil { return nil, fmt.Errorf("HKDF derivation failed: %w", err) diff --git a/server/database/database.go b/server/database/database.go index 096d2a8..9b9674b 100644 --- a/server/database/database.go +++ b/server/database/database.go @@ -12,7 +12,7 @@ import ( _ "github.com/lib/pq" _ "modernc.org/sqlite" - "switchboard-core/config" + "armature/config" ) // DB is the application-wide database connection pool. @@ -80,7 +80,7 @@ func connectPostgres(dsn string) error { // connectSQLite opens a SQLite database with WAL mode enabled. func connectSQLite(dsn string) error { if dsn == "" { - dsn = "switchboard.db" + dsn = "armature.db" } // Ensure parent directory exists for file-based DBs. diff --git a/server/database/testhelper.go b/server/database/testhelper.go index 495b530..cc5eab1 100644 --- a/server/database/testhelper.go +++ b/server/database/testhelper.go @@ -17,7 +17,7 @@ import ( // Use SetupTestDB in TestMain and pass this to subtests. var TestDB *sql.DB -const testDBName = "switchboard_core_ci" +const testDBName = "armature_ci" // SetupTestDB connects to the appropriate database backend (Postgres or // SQLite based on DB_DRIVER env), runs migrations, and sets database.DB @@ -50,7 +50,7 @@ func setupSQLiteTestDB() func() { CurrentDialect = DialectSQLite // Use a temp file so multiple connections share the same DB. - tmpFile, err := os.CreateTemp("", "switchboard-test-*.db") + tmpFile, err := os.CreateTemp("", "armature-test-*.db") if err != nil { log.Printf("⚠ Cannot create temp SQLite DB: %v — skipping DB tests", err) return func() {} @@ -337,7 +337,7 @@ func TruncateAll(t *testing.T) { DB.Exec(` INSERT INTO global_settings (key, value) VALUES ('registration', '{"enabled": true}'), - ('site', '{"name": "Switchboard Core", "tagline": "Self-hosted extension platform"}'), + ('site', '{"name": "Armature", "tagline": "Self-hosted extension platform"}'), ('banner', '{"enabled": false, "text": "", "bg": "#007a33", "fg": "#ffffff"}') ON CONFLICT (key) DO NOTHING `) @@ -351,7 +351,7 @@ func TruncateAll(t *testing.T) { DB.Exec(` INSERT INTO global_settings (key, value) VALUES ('registration', '{"enabled": true}'::jsonb), - ('site', '{"name": "Switchboard Core", "tagline": "Self-hosted extension platform"}'::jsonb), + ('site', '{"name": "Armature", "tagline": "Self-hosted extension platform"}'::jsonb), ('banner', '{"enabled": false, "text": "", "bg": "#007a33", "fg": "#ffffff"}'::jsonb) ON CONFLICT (key) DO NOTHING `) diff --git a/server/events/pg_broadcast.go b/server/events/pg_broadcast.go index 281898e..274547b 100644 --- a/server/events/pg_broadcast.go +++ b/server/events/pg_broadcast.go @@ -23,10 +23,10 @@ import ( "github.com/lib/pq" - "switchboard-core/database" + "armature/database" ) -const pgNotifyChannel = "switchboard_events" +const pgNotifyChannel = "armature_events" // maxNotifyBytes is the practical PG NOTIFY payload limit (8192 byte hard limit, // leave headroom for encoding overhead). diff --git a/server/events/ticket_adapter.go b/server/events/ticket_adapter.go index 956bd9e..cc7244b 100644 --- a/server/events/ticket_adapter.go +++ b/server/events/ticket_adapter.go @@ -7,7 +7,7 @@ import ( "context" "time" - "switchboard-core/store" + "armature/store" ) // TicketValidatorAdapter satisfies middleware.TicketValidator using a diff --git a/server/events/ws.go b/server/events/ws.go index 06c6a56..46d83ae 100644 --- a/server/events/ws.go +++ b/server/events/ws.go @@ -11,7 +11,7 @@ import ( "github.com/gin-gonic/gin" "github.com/gorilla/websocket" - "switchboard-core/metrics" + "armature/metrics" ) const ( diff --git a/server/go.mod b/server/go.mod index fe94346..0af9d3b 100644 --- a/server/go.mod +++ b/server/go.mod @@ -1,4 +1,4 @@ -module switchboard-core +module armature go 1.23.0 @@ -14,7 +14,7 @@ require ( github.com/robfig/cron/v3 v3.0.1 go.starlark.net v0.0.0-20260210143700-b62fd896b91b golang.org/x/crypto v0.41.0 - golang.org/x/net v0.43.0 + gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.34.5 ) @@ -52,10 +52,10 @@ require ( github.com/ugorji/go/codec v1.2.11 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect golang.org/x/arch v0.3.0 // indirect + golang.org/x/net v0.43.0 // indirect golang.org/x/sys v0.35.0 // indirect golang.org/x/text v0.28.0 // indirect google.golang.org/protobuf v1.36.8 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect modernc.org/libc v1.55.3 // indirect modernc.org/mathutil v1.6.0 // indirect modernc.org/memory v1.8.0 // indirect diff --git a/server/go.sum b/server/go.sum index 56dc482..3ab77ee 100644 --- a/server/go.sum +++ b/server/go.sum @@ -135,8 +135,6 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= diff --git a/server/handlers/admin.go b/server/handlers/admin.go index 3b230b1..b0458da 100644 --- a/server/handlers/admin.go +++ b/server/handlers/admin.go @@ -10,12 +10,12 @@ import ( "github.com/gin-gonic/gin" "golang.org/x/crypto/bcrypt" - "switchboard-core/auth" - "switchboard-core/crypto" - "switchboard-core/database" - "switchboard-core/models" - "switchboard-core/storage" - "switchboard-core/store" + "armature/auth" + "armature/crypto" + "armature/database" + "armature/models" + "armature/storage" + "armature/store" ) type AdminHandler struct { diff --git a/server/handlers/admin_connections.go b/server/handlers/admin_connections.go index f624613..1ec68c6 100644 --- a/server/handlers/admin_connections.go +++ b/server/handlers/admin_connections.go @@ -7,7 +7,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/models" + "armature/models" ) // ListGlobalConnections returns all global-scope connections. diff --git a/server/handlers/admin_email.go b/server/handlers/admin_email.go index ca36bbb..65c5b92 100644 --- a/server/handlers/admin_email.go +++ b/server/handlers/admin_email.go @@ -6,8 +6,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/notifications" - "switchboard-core/store" + "armature/notifications" + "armature/store" ) // AdminEmailHandler handles admin SMTP configuration and test endpoints. @@ -53,7 +53,7 @@ func (h *AdminEmailHandler) TestEmail(c *gin.Context) { } // Get instance name - instanceName := "Switchboard Core" + instanceName := "Armature" if branding, err := h.stores.GlobalConfig.Get(context.Background(), "branding"); err == nil { if name, ok := branding["instance_name"].(string); ok && name != "" { instanceName = name @@ -64,7 +64,7 @@ func (h *AdminEmailHandler) TestEmail(c *gin.Context) { htmlBody := `

` + instanceName + `

-

This is a test email from your Switchboard Core instance.

+

This is a test email from your Armature instance.

If you received this, your SMTP configuration is working correctly.

` textBody := instanceName + " — Test Email\n\nThis is a test email. Your SMTP configuration is working correctly." diff --git a/server/handlers/admin_metrics.go b/server/handlers/admin_metrics.go index 435401e..d1d064f 100644 --- a/server/handlers/admin_metrics.go +++ b/server/handlers/admin_metrics.go @@ -3,7 +3,7 @@ package handlers import ( "github.com/gin-gonic/gin" - "switchboard-core/metrics" + "armature/metrics" ) // MetricsHandler serves the admin metrics endpoint. diff --git a/server/handlers/admin_metrics_test.go b/server/handlers/admin_metrics_test.go index 0285dbe..914ff9a 100644 --- a/server/handlers/admin_metrics_test.go +++ b/server/handlers/admin_metrics_test.go @@ -9,8 +9,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/metrics" - "switchboard-core/store" + "armature/metrics" + "armature/store" ) // mockHub implements metrics.ConnCounter for tests. diff --git a/server/handlers/audit.go b/server/handlers/audit.go index cf3df93..853bf4f 100644 --- a/server/handlers/audit.go +++ b/server/handlers/audit.go @@ -8,8 +8,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/models" - "switchboard-core/store" + "armature/models" + "armature/store" ) // AuditLog writes an audit entry via the store interface. diff --git a/server/handlers/auth.go b/server/handlers/auth.go index 4b88b0c..8195be5 100644 --- a/server/handlers/auth.go +++ b/server/handlers/auth.go @@ -17,11 +17,11 @@ import ( "github.com/google/uuid" "golang.org/x/crypto/bcrypt" - "switchboard-core/auth" - "switchboard-core/config" - "switchboard-core/crypto" - "switchboard-core/models" - "switchboard-core/store" + "armature/auth" + "armature/config" + "armature/crypto" + "armature/models" + "armature/store" ) // Claims represents the JWT payload. @@ -517,7 +517,7 @@ func BootstrapAdmin(cfg *config.Config, s store.Stores, uekCache ...*crypto.UEKC email := cfg.AdminEmail if email == "" { - email = cfg.AdminUsername + "@switchboard.local" + email = cfg.AdminUsername + "@armature.local" } handle := auth.UniqueHandle(ctx, s.Users, models.HandleFromName(cfg.AdminUsername)) @@ -612,7 +612,7 @@ func SeedUsers(cfg *config.Config, s store.Stores, uekCache ...*crypto.UEKCache) handle := auth.UniqueHandle(ctx, s.Users, models.HandleFromName(username)) user := &models.User{ Username: username, - Email: username + "@switchboard.local", + Email: username + "@armature.local", PasswordHash: string(hash), IsActive: true, AuthSource: "builtin", diff --git a/server/handlers/auth_test.go b/server/handlers/auth_test.go index 53de73c..78777b2 100644 --- a/server/handlers/auth_test.go +++ b/server/handlers/auth_test.go @@ -14,9 +14,9 @@ import ( "github.com/golang-jwt/jwt/v5" "golang.org/x/crypto/bcrypt" - "switchboard-core/auth" - "switchboard-core/config" - "switchboard-core/store" + "armature/auth" + "armature/config" + "armature/store" ) func testConfig() *config.Config { @@ -43,7 +43,7 @@ func TestJWTGeneration(t *testing.T) { RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(now), ExpiresAt: jwt.NewNumericDate(now.Add(15 * time.Minute)), - Issuer: "switchboard-core", + Issuer: "armature", }, } diff --git a/server/handlers/backup.go b/server/handlers/backup.go index 1cd4c6b..0fe0026 100644 --- a/server/handlers/backup.go +++ b/server/handlers/backup.go @@ -16,8 +16,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/database" - "switchboard-core/store" + "armature/database" + "armature/store" ) // BackupHandler provides backup/restore endpoints for admin users. @@ -101,7 +101,7 @@ func (h *BackupHandler) CreateBackup(c *gin.Context) { } // Stream directly to client - filename := fmt.Sprintf("switchboard-%s-%s.swb", meta.Version, meta.Timestamp.Format("20060102-150405")) + filename := fmt.Sprintf("armature-%s-%s.swb", meta.Version, meta.Timestamp.Format("20060102-150405")) c.Header("Content-Type", "application/zip") c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%q", filename)) @@ -119,7 +119,7 @@ func (h *BackupHandler) createStoredBackup(c *gin.Context, ctx interface{ Deadli } os.MkdirAll(dir, 0755) - filename := fmt.Sprintf("switchboard-%s-%s.swb", meta.Version, meta.Timestamp.Format("20060102-150405")) + filename := fmt.Sprintf("armature-%s-%s.swb", meta.Version, meta.Timestamp.Format("20060102-150405")) path := filepath.Join(dir, filename) f, err := os.Create(path) diff --git a/server/handlers/backup_tables.go b/server/handlers/backup_tables.go index 5f0fe52..1ad2c54 100644 --- a/server/handlers/backup_tables.go +++ b/server/handlers/backup_tables.go @@ -10,7 +10,7 @@ import ( "log" "strings" - "switchboard-core/database" + "armature/database" ) // coreTableOrder lists tables for backup in FK-safe import order. diff --git a/server/handlers/backup_test.go b/server/handlers/backup_test.go index 0c414f5..815a049 100644 --- a/server/handlers/backup_test.go +++ b/server/handlers/backup_test.go @@ -13,12 +13,12 @@ import ( "github.com/gin-gonic/gin" - authpkg "switchboard-core/auth" - "switchboard-core/config" - "switchboard-core/database" - "switchboard-core/middleware" - "switchboard-core/store" - "switchboard-core/store/sqlite" + authpkg "armature/auth" + "armature/config" + "armature/database" + "armature/middleware" + "armature/store" + "armature/store/sqlite" ) // ── Backup Test Harness ──────────────────────── diff --git a/server/handlers/cluster.go b/server/handlers/cluster.go index b63f7e0..d0fbfec 100644 --- a/server/handlers/cluster.go +++ b/server/handlers/cluster.go @@ -5,7 +5,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/store" + "armature/store" ) // ClusterHandler serves the admin cluster API. diff --git a/server/handlers/cluster_test.go b/server/handlers/cluster_test.go index 208ee07..1d87956 100644 --- a/server/handlers/cluster_test.go +++ b/server/handlers/cluster_test.go @@ -10,7 +10,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/store" + "armature/store" ) // mockClusterStore implements store.ClusterStore for handler tests. diff --git a/server/handlers/connection_resolver.go b/server/handlers/connection_resolver.go index 473c889..bc1282c 100644 --- a/server/handlers/connection_resolver.go +++ b/server/handlers/connection_resolver.go @@ -6,9 +6,9 @@ import ( "context" "encoding/json" - "switchboard-core/crypto" - "switchboard-core/models" - "switchboard-core/store" + "armature/crypto" + "armature/models" + "armature/store" ) // ConnectionResolverAdapter implements sandbox.ConnectionResolver. diff --git a/server/handlers/connection_types.go b/server/handlers/connection_types.go index cfbd1f7..5397ca0 100644 --- a/server/handlers/connection_types.go +++ b/server/handlers/connection_types.go @@ -9,7 +9,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/store" + "armature/store" ) // ConnectionTypeEntry is a single connection type in the API response. diff --git a/server/handlers/connections.go b/server/handlers/connections.go index b8e0fbd..1a700fd 100644 --- a/server/handlers/connections.go +++ b/server/handlers/connections.go @@ -9,9 +9,9 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/crypto" - "switchboard-core/models" - "switchboard-core/store" + "armature/crypto" + "armature/models" + "armature/store" ) // ConnectionHandler handles user-facing extension connection endpoints. diff --git a/server/handlers/dependencies.go b/server/handlers/dependencies.go index 3497a74..e663b7f 100644 --- a/server/handlers/dependencies.go +++ b/server/handlers/dependencies.go @@ -5,7 +5,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/models" + "armature/models" ) // ── Extension Dependencies ────────────────── diff --git a/server/handlers/ext_api.go b/server/handlers/ext_api.go index 37d2b2e..af68353 100644 --- a/server/handlers/ext_api.go +++ b/server/handlers/ext_api.go @@ -41,9 +41,9 @@ import ( "github.com/gin-gonic/gin" "go.starlark.net/starlark" - "switchboard-core/models" - "switchboard-core/sandbox" - "switchboard-core/store" + "armature/models" + "armature/sandbox" + "armature/store" ) // ExtAPIHandler serves extension API routes via Starlark. diff --git a/server/handlers/ext_db_schema.go b/server/handlers/ext_db_schema.go index aadb49a..bf22415 100644 --- a/server/handlers/ext_db_schema.go +++ b/server/handlers/ext_db_schema.go @@ -15,7 +15,7 @@ import ( "regexp" "strings" - "switchboard-core/store" + "armature/store" ) // validSchemaIdentifier matches safe SQL identifiers for table and column names. diff --git a/server/handlers/ext_db_schema_test.go b/server/handlers/ext_db_schema_test.go index 4eebdab..62a055d 100644 --- a/server/handlers/ext_db_schema_test.go +++ b/server/handlers/ext_db_schema_test.go @@ -8,7 +8,7 @@ import ( _ "modernc.org/sqlite" - "switchboard-core/store" + "armature/store" ) // ── mock ExtDataStore ──────────────────────────────────────────────────────── diff --git a/server/handlers/extension_permissions.go b/server/handlers/extension_permissions.go index 8ae3521..bacde38 100644 --- a/server/handlers/extension_permissions.go +++ b/server/handlers/extension_permissions.go @@ -5,8 +5,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/models" - "switchboard-core/store" + "armature/models" + "armature/store" ) // ExtPermHandler serves extension permission management endpoints. diff --git a/server/handlers/extension_secrets.go b/server/handlers/extension_secrets.go index 0d98b4e..4abcf3d 100644 --- a/server/handlers/extension_secrets.go +++ b/server/handlers/extension_secrets.go @@ -5,8 +5,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/models" - "switchboard-core/store" + "armature/models" + "armature/store" ) // ExtSecretsHandler serves extension secret management endpoints. diff --git a/server/handlers/extension_test.go b/server/handlers/extension_test.go index b5450c4..c0ee7b0 100644 --- a/server/handlers/extension_test.go +++ b/server/handlers/extension_test.go @@ -9,13 +9,13 @@ import ( "github.com/gin-gonic/gin" - authpkg "switchboard-core/auth" - "switchboard-core/config" - "switchboard-core/database" - "switchboard-core/middleware" - "switchboard-core/store" - postgres "switchboard-core/store/postgres" - sqlite "switchboard-core/store/sqlite" + authpkg "armature/auth" + "armature/config" + "armature/database" + "armature/middleware" + "armature/store" + postgres "armature/store/postgres" + sqlite "armature/store/sqlite" ) // ── Extension Test Harness ────────────────── diff --git a/server/handlers/extensions.go b/server/handlers/extensions.go index 91fcd96..3bfda80 100644 --- a/server/handlers/extensions.go +++ b/server/handlers/extensions.go @@ -9,10 +9,10 @@ import ( "strings" - "switchboard-core/database" - "switchboard-core/models" - "switchboard-core/store" - "switchboard-core/triggers" + "armature/database" + "armature/models" + "armature/store" + "armature/triggers" ) // ExtensionHandler serves extension management endpoints. diff --git a/server/handlers/groups.go b/server/handlers/groups.go index 27bc7db..addcd1b 100644 --- a/server/handlers/groups.go +++ b/server/handlers/groups.go @@ -7,11 +7,11 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/auth" - "switchboard-core/database" - "switchboard-core/models" - "switchboard-core/notifications" - "switchboard-core/store" + "armature/auth" + "armature/database" + "armature/models" + "armature/notifications" + "armature/store" ) // ── Request types ─────────────────────────── diff --git a/server/handlers/notification_test.go b/server/handlers/notification_test.go index 242f4a3..6f3de55 100644 --- a/server/handlers/notification_test.go +++ b/server/handlers/notification_test.go @@ -8,14 +8,14 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/config" - "switchboard-core/database" - "switchboard-core/middleware" - "switchboard-core/models" - "switchboard-core/notifications" - "switchboard-core/store" - postgres "switchboard-core/store/postgres" - sqlite "switchboard-core/store/sqlite" + "armature/config" + "armature/database" + "armature/middleware" + "armature/models" + "armature/notifications" + "armature/store" + postgres "armature/store/postgres" + sqlite "armature/store/sqlite" ) // ── Notification Test Harness ────────────── diff --git a/server/handlers/notifications.go b/server/handlers/notifications.go index be84441..6a89f22 100644 --- a/server/handlers/notifications.go +++ b/server/handlers/notifications.go @@ -10,10 +10,10 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/events" - "switchboard-core/models" - "switchboard-core/notifications" - "switchboard-core/store" + "armature/events" + "armature/models" + "armature/notifications" + "armature/store" ) // ── Handler ───────────────────────────────── diff --git a/server/handlers/openapi.go b/server/handlers/openapi.go index 068e290..f2d1ef7 100644 --- a/server/handlers/openapi.go +++ b/server/handlers/openapi.go @@ -14,7 +14,7 @@ import ( "log" "strings" - "switchboard-core/store" + "armature/store" "gopkg.in/yaml.v3" ) diff --git a/server/handlers/openapi_test.go b/server/handlers/openapi_test.go index 806ed75..d7c67c4 100644 --- a/server/handlers/openapi_test.go +++ b/server/handlers/openapi_test.go @@ -5,17 +5,17 @@ import ( "encoding/json" "testing" - "switchboard-core/database" - "switchboard-core/store" - postgres "switchboard-core/store/postgres" - sqlite "switchboard-core/store/sqlite" + "armature/database" + "armature/store" + postgres "armature/store/postgres" + sqlite "armature/store/sqlite" ) // Minimal static spec for testing — valid OpenAPI 3.0 structure. var testStaticSpec = []byte(` openapi: "3.0.3" info: - title: Switchboard Core + title: Armature version: "${VERSION}" paths: /api/v1/health: diff --git a/server/handlers/package_dormant_test.go b/server/handlers/package_dormant_test.go index 577a317..8b19fc2 100644 --- a/server/handlers/package_dormant_test.go +++ b/server/handlers/package_dormant_test.go @@ -8,13 +8,13 @@ import ( "github.com/gin-gonic/gin" - authpkg "switchboard-core/auth" - "switchboard-core/config" - "switchboard-core/database" - "switchboard-core/middleware" - "switchboard-core/store" - postgres "switchboard-core/store/postgres" - sqlite "switchboard-core/store/sqlite" + authpkg "armature/auth" + "armature/config" + "armature/database" + "armature/middleware" + "armature/store" + postgres "armature/store/postgres" + sqlite "armature/store/sqlite" ) // ── Dormant Test Harness ────────────────────── diff --git a/server/handlers/package_registry.go b/server/handlers/package_registry.go index 58480e1..2b38dc0 100644 --- a/server/handlers/package_registry.go +++ b/server/handlers/package_registry.go @@ -29,7 +29,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/store" + "armature/store" ) // RegistryEntry represents a single package in the registry. diff --git a/server/handlers/packages.go b/server/handlers/packages.go index be7b66b..3563e62 100644 --- a/server/handlers/packages.go +++ b/server/handlers/packages.go @@ -15,11 +15,11 @@ import ( "github.com/gin-gonic/gin" "go.starlark.net/starlark" - "switchboard-core/database" - "switchboard-core/models" - "switchboard-core/sandbox" - "switchboard-core/store" - "switchboard-core/triggers" + "armature/database" + "armature/models" + "armature/sandbox" + "armature/store" + "armature/triggers" ) // validPackageID matches lowercase alphanumeric slugs with optional hyphens. diff --git a/server/handlers/packages_bundled.go b/server/handlers/packages_bundled.go index 43ad29d..7b342eb 100644 --- a/server/handlers/packages_bundled.go +++ b/server/handlers/packages_bundled.go @@ -15,11 +15,11 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/database" - "switchboard-core/models" - "switchboard-core/sandbox" - "switchboard-core/store" - "switchboard-core/triggers" + "armature/database" + "armature/models" + "armature/sandbox" + "armature/store" + "armature/triggers" ) // defaultBundledPackages is the curated set of packages installed by default. diff --git a/server/handlers/packages_bundled_test.go b/server/handlers/packages_bundled_test.go index 88162e1..98ac2fa 100644 --- a/server/handlers/packages_bundled_test.go +++ b/server/handlers/packages_bundled_test.go @@ -8,10 +8,10 @@ import ( "path/filepath" "testing" - "switchboard-core/database" - "switchboard-core/store" - postgres "switchboard-core/store/postgres" - sqlite "switchboard-core/store/sqlite" + "armature/database" + "armature/store" + postgres "armature/store/postgres" + sqlite "armature/store/sqlite" ) // ── Test helpers ────────────────────────────── diff --git a/server/handlers/packages_update_test.go b/server/handlers/packages_update_test.go index 933732a..d8c06dd 100644 --- a/server/handlers/packages_update_test.go +++ b/server/handlers/packages_update_test.go @@ -15,8 +15,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/database" - "switchboard-core/store" + "armature/database" + "armature/store" ) // ── Helpers ──────────────────────────────────── diff --git a/server/handlers/presence.go b/server/handlers/presence.go index 382e556..edb3979 100644 --- a/server/handlers/presence.go +++ b/server/handlers/presence.go @@ -14,7 +14,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/store" + "armature/store" ) const presenceOnlineThreshold = 90 * time.Second diff --git a/server/handlers/profile_bootstrap.go b/server/handlers/profile_bootstrap.go index 9e3e3dc..8a9417d 100644 --- a/server/handlers/profile_bootstrap.go +++ b/server/handlers/profile_bootstrap.go @@ -15,8 +15,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/auth" - "switchboard-core/store" + "armature/auth" + "armature/store" ) // ProfileBootstrapHandler serves the combined boot payload. diff --git a/server/handlers/profile_permissions.go b/server/handlers/profile_permissions.go index 1db1e10..32687e3 100644 --- a/server/handlers/profile_permissions.go +++ b/server/handlers/profile_permissions.go @@ -6,8 +6,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/auth" - "switchboard-core/store" + "armature/auth" + "armature/store" ) // ProfilePermissionsHandler exposes the current user's resolved permissions. diff --git a/server/handlers/safe_json.go b/server/handlers/safe_json.go index 6110d8d..1d8082f 100644 --- a/server/handlers/safe_json.go +++ b/server/handlers/safe_json.go @@ -8,7 +8,7 @@ import ( "github.com/gin-gonic/gin" "github.com/lib/pq" - "switchboard-core/database" + "armature/database" ) // ── SafeJSON ──────────────────────────────── diff --git a/server/handlers/schedules.go b/server/handlers/schedules.go index e049c34..05f7bd3 100644 --- a/server/handlers/schedules.go +++ b/server/handlers/schedules.go @@ -7,9 +7,9 @@ import ( "github.com/gin-gonic/gin" "github.com/robfig/cron/v3" - "switchboard-core/models" - "switchboard-core/store" - "switchboard-core/triggers" + "armature/models" + "armature/store" + "armature/triggers" ) // ScheduleHandler provides CRUD for user-created scheduled tasks. diff --git a/server/handlers/settings.go b/server/handlers/settings.go index 1fdd8c2..bd96c48 100644 --- a/server/handlers/settings.go +++ b/server/handlers/settings.go @@ -14,8 +14,8 @@ import ( "github.com/gin-gonic/gin" "golang.org/x/crypto/bcrypt" - "switchboard-core/crypto" - "switchboard-core/store" + "armature/crypto" + "armature/store" ) // ── Request Types ─────────────────────────── diff --git a/server/handlers/storage.go b/server/handlers/storage.go index d8662dd..c7aa8aa 100644 --- a/server/handlers/storage.go +++ b/server/handlers/storage.go @@ -5,7 +5,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/storage" + "armature/storage" ) // storageConfigured is a package-level flag set during init. diff --git a/server/handlers/team_connections.go b/server/handlers/team_connections.go index ad4c34b..ae2351e 100644 --- a/server/handlers/team_connections.go +++ b/server/handlers/team_connections.go @@ -8,7 +8,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/models" + "armature/models" ) // ListTeamConnections returns connections scoped to a team. diff --git a/server/handlers/team_package_settings.go b/server/handlers/team_package_settings.go index 3d63e4c..22d56ac 100644 --- a/server/handlers/team_package_settings.go +++ b/server/handlers/team_package_settings.go @@ -12,7 +12,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/store" + "armature/store" ) // TeamPackageSettingsHandler serves team-scoped package settings endpoints. diff --git a/server/handlers/teams.go b/server/handlers/teams.go index 41e0a0d..9d2c856 100644 --- a/server/handlers/teams.go +++ b/server/handlers/teams.go @@ -10,10 +10,10 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/crypto" - "switchboard-core/database" - "switchboard-core/models" - "switchboard-core/store" + "armature/crypto" + "armature/database" + "armature/models" + "armature/store" ) // ── Request types ─────────────────────────── diff --git a/server/handlers/test_helpers_test.go b/server/handlers/test_helpers_test.go index 306a4d2..34c9264 100644 --- a/server/handlers/test_helpers_test.go +++ b/server/handlers/test_helpers_test.go @@ -13,7 +13,7 @@ import ( "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" - "switchboard-core/database" + "armature/database" ) const testJWTSecret = "test-secret-key-for-handler-tests" @@ -48,7 +48,7 @@ func makeToken(userID, email, _ string) string { RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(time.Now()), ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)), - Issuer: "switchboard-core", + Issuer: "armature", }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) diff --git a/server/handlers/testmain_test.go b/server/handlers/testmain_test.go index a58e692..e6b06b2 100644 --- a/server/handlers/testmain_test.go +++ b/server/handlers/testmain_test.go @@ -6,7 +6,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/database" + "armature/database" _ "modernc.org/sqlite" // register sqlite driver for DB_DRIVER=sqlite ) diff --git a/server/handlers/trigger_sync.go b/server/handlers/trigger_sync.go index 48a1594..1447d7a 100644 --- a/server/handlers/trigger_sync.go +++ b/server/handlers/trigger_sync.go @@ -7,9 +7,9 @@ import ( "encoding/json" "log" - "switchboard-core/models" - "switchboard-core/store" - "switchboard-core/triggers" + "armature/models" + "armature/store" + "armature/triggers" ) // manifestTrigger is the manifest.json trigger declaration shape. diff --git a/server/handlers/triggers.go b/server/handlers/triggers.go index 0cc0f37..39963ea 100644 --- a/server/handlers/triggers.go +++ b/server/handlers/triggers.go @@ -6,8 +6,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/store" - "switchboard-core/triggers" + "armature/store" + "armature/triggers" ) // TriggerHandler provides admin CRUD for extension-declared triggers. diff --git a/server/handlers/upgrade_test.go b/server/handlers/upgrade_test.go index 82a2d72..eb37650 100644 --- a/server/handlers/upgrade_test.go +++ b/server/handlers/upgrade_test.go @@ -8,9 +8,9 @@ import ( "net/http" "testing" - "switchboard-core/database" - "switchboard-core/models" - "switchboard-core/store" + "armature/database" + "armature/models" + "armature/store" ) // ═══════════════════════════════════════════════ diff --git a/server/handlers/user_packages.go b/server/handlers/user_packages.go index 94b625d..43c137d 100644 --- a/server/handlers/user_packages.go +++ b/server/handlers/user_packages.go @@ -22,7 +22,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/store" + "armature/store" ) // allowedNonGlobalPermissions is the set of permissions that non-admin diff --git a/server/handlers/workflow_assignment_handlers.go b/server/handlers/workflow_assignment_handlers.go index 70d9470..d6280b9 100644 --- a/server/handlers/workflow_assignment_handlers.go +++ b/server/handlers/workflow_assignment_handlers.go @@ -7,9 +7,9 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/models" - "switchboard-core/store" - "switchboard-core/workflow" + "armature/models" + "armature/store" + "armature/workflow" ) // ── Assignment Handlers ───────────────────── diff --git a/server/handlers/workflow_engine_test.go b/server/handlers/workflow_engine_test.go index e463e5c..4857b52 100644 --- a/server/handlers/workflow_engine_test.go +++ b/server/handlers/workflow_engine_test.go @@ -6,9 +6,9 @@ import ( "strings" "testing" - "switchboard-core/database" - "switchboard-core/models" - "switchboard-core/workflow" + "armature/database" + "armature/models" + "armature/workflow" ) // testEngine creates a workflow engine with nil bus and runner (safe for tests). diff --git a/server/handlers/workflow_hooks.go b/server/handlers/workflow_hooks.go index 86cc1d6..1b96d88 100644 --- a/server/handlers/workflow_hooks.go +++ b/server/handlers/workflow_hooks.go @@ -7,8 +7,8 @@ import ( "go.starlark.net/starlark" - "switchboard-core/sandbox" - "switchboard-core/store" + "armature/sandbox" + "armature/store" ) // ── on_advance Hook ───────────────────────── diff --git a/server/handlers/workflow_instance_handlers.go b/server/handlers/workflow_instance_handlers.go index b233231..a0ff373 100644 --- a/server/handlers/workflow_instance_handlers.go +++ b/server/handlers/workflow_instance_handlers.go @@ -8,8 +8,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/store" - "switchboard-core/workflow" + "armature/store" + "armature/workflow" ) // ── Instance Handlers ─────────────────────── diff --git a/server/handlers/workflow_packages.go b/server/handlers/workflow_packages.go index a65643a..cb157b3 100644 --- a/server/handlers/workflow_packages.go +++ b/server/handlers/workflow_packages.go @@ -15,8 +15,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/models" - "switchboard-core/store" + "armature/models" + "armature/store" ) // WorkflowPackageHandler handles workflow package export and install. diff --git a/server/handlers/workflow_public_handlers.go b/server/handlers/workflow_public_handlers.go index 0a65150..d625644 100644 --- a/server/handlers/workflow_public_handlers.go +++ b/server/handlers/workflow_public_handlers.go @@ -7,8 +7,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/store" - "switchboard-core/workflow" + "armature/store" + "armature/workflow" ) // ── Public Workflow Handlers ─────── diff --git a/server/handlers/workflow_signoff_handlers.go b/server/handlers/workflow_signoff_handlers.go index 14dfc92..76eafb4 100644 --- a/server/handlers/workflow_signoff_handlers.go +++ b/server/handlers/workflow_signoff_handlers.go @@ -5,9 +5,9 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/models" - "switchboard-core/store" - "switchboard-core/workflow" + "armature/models" + "armature/store" + "armature/workflow" ) // ── Signoff Handlers ───────────────── diff --git a/server/handlers/workflow_store_test.go b/server/handlers/workflow_store_test.go index ad0cd39..61414d6 100644 --- a/server/handlers/workflow_store_test.go +++ b/server/handlers/workflow_store_test.go @@ -7,11 +7,11 @@ import ( "testing" "time" - "switchboard-core/database" - "switchboard-core/models" - "switchboard-core/store" - "switchboard-core/store/postgres" - "switchboard-core/store/sqlite" + "armature/database" + "armature/models" + "armature/store" + "armature/store/postgres" + "armature/store/sqlite" ) // jsonEq compares two JSON byte slices ignoring whitespace and key order diff --git a/server/handlers/workflow_team.go b/server/handlers/workflow_team.go index b7d00ed..396718d 100644 --- a/server/handlers/workflow_team.go +++ b/server/handlers/workflow_team.go @@ -6,7 +6,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/models" + "armature/models" ) // ── Team-Scoped Workflow Wrappers ──────────────── diff --git a/server/handlers/workflows.go b/server/handlers/workflows.go index dc2a598..ad0c953 100644 --- a/server/handlers/workflows.go +++ b/server/handlers/workflows.go @@ -10,8 +10,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/models" - "switchboard-core/store" + "armature/models" + "armature/store" ) // ── Workflow Handler ──────────────────────── diff --git a/server/logging/logger.go b/server/logging/logger.go index 619a2a7..209d0cf 100644 --- a/server/logging/logger.go +++ b/server/logging/logger.go @@ -1,5 +1,5 @@ // Package logging provides structured logging configuration for -// Switchboard Core using the standard library's log/slog package. +// Armature using the standard library's log/slog package. // Configured via LOG_FORMAT ("text"|"json") and LOG_LEVEL env vars. package logging diff --git a/server/main.go b/server/main.go index 8ce3c0e..edf99b2 100644 --- a/server/main.go +++ b/server/main.go @@ -15,25 +15,25 @@ import ( "github.com/gin-gonic/gin" "github.com/prometheus/client_golang/prometheus/promhttp" - "switchboard-core/auth" - "switchboard-core/cluster" - "switchboard-core/config" - "switchboard-core/crypto" - "switchboard-core/database" - "switchboard-core/events" - "switchboard-core/sandbox" - "switchboard-core/handlers" - "switchboard-core/logging" - "switchboard-core/metrics" - "switchboard-core/middleware" - "switchboard-core/notifications" - "switchboard-core/pages" - "switchboard-core/storage" - "switchboard-core/store" - "switchboard-core/triggers" - "switchboard-core/workflow" - postgres "switchboard-core/store/postgres" - sqliteStore "switchboard-core/store/sqlite" + "armature/auth" + "armature/cluster" + "armature/config" + "armature/crypto" + "armature/database" + "armature/events" + "armature/sandbox" + "armature/handlers" + "armature/logging" + "armature/metrics" + "armature/middleware" + "armature/notifications" + "armature/pages" + "armature/storage" + "armature/store" + "armature/triggers" + "armature/workflow" + postgres "armature/store/postgres" + sqliteStore "armature/store/sqlite" ) // @@ -45,13 +45,13 @@ var swaggerHTML []byte func main() { // ── Subcommand dispatch ────────────────── - // Usage: switchboard vault rekey + // Usage: armature vault rekey if len(os.Args) > 2 && os.Args[1] == "vault" { runVaultCommand(os.Args[2]) return } if len(os.Args) > 1 && os.Args[1] == "version" { - fmt.Println("switchboard", Version) + fmt.Println("armature", Version) return } @@ -262,7 +262,7 @@ func main() { if stores.GlobalConfig != nil { if smtpCfg, err := notifications.LoadSMTPConfig(stores.GlobalConfig, keyResolver); err == nil && smtpCfg != nil { transport := notifications.NewEmailTransport(*smtpCfg) - instanceName := "Switchboard Core" + instanceName := "Armature" if brandCfg, err := stores.GlobalConfig.Get(context.Background(), "branding"); err == nil { if name, ok := brandCfg["instance_name"].(string); ok && name != "" { instanceName = name @@ -899,7 +899,7 @@ func main() { if bp == "" { bp = "/" } - log.Printf("🔀 Switchboard Core v%s starting on port %s", Version, cfg.Port) + log.Printf("⚙ Armature v%s starting on port %s", Version, cfg.Port) log.Printf(" Base path: %s", bp) log.Printf(" Schema: %s", database.SchemaVersion()) if objStore != nil { @@ -1014,7 +1014,7 @@ func runVaultCommand(subcmd string) { default: fmt.Fprintf(os.Stderr, "Unknown vault command: %s\n", subcmd) - fmt.Fprintf(os.Stderr, "Usage: switchboard vault \n") + fmt.Fprintf(os.Stderr, "Usage: armature vault \n") os.Exit(1) } } diff --git a/server/metrics/collector.go b/server/metrics/collector.go index d9ffd03..46cb4af 100644 --- a/server/metrics/collector.go +++ b/server/metrics/collector.go @@ -12,8 +12,8 @@ import ( "runtime" "time" - "switchboard-core/database" - "switchboard-core/store" + "armature/database" + "armature/store" ) // Snapshot is the top-level JSON response from GET /api/v1/admin/metrics. diff --git a/server/metrics/metrics.go b/server/metrics/metrics.go index 5727d85..747594b 100644 --- a/server/metrics/metrics.go +++ b/server/metrics/metrics.go @@ -1,5 +1,5 @@ -// Package metrics defines all Prometheus metrics for Switchboard Core. -// All metrics use the "switchboard_" prefix. Registered via promauto +// Package metrics defines all Prometheus metrics for Armature Core. +// All metrics use the "armature_" prefix. Registered via promauto // so they are available on the default registry's /metrics endpoint. package metrics @@ -12,12 +12,12 @@ import ( var ( HTTPRequestsTotal = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "switchboard_http_requests_total", + Name: "armature_http_requests_total", Help: "Total HTTP requests by method, path pattern, and status code.", }, []string{"method", "path_pattern", "status"}) HTTPRequestDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "switchboard_http_request_duration_seconds", + Name: "armature_http_request_duration_seconds", Help: "HTTP request latency in seconds.", Buckets: []float64{0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30}, }, []string{"method", "path_pattern"}) @@ -26,7 +26,7 @@ var ( // ── WebSocket Metrics ─────────────────────── var WebSocketConnections = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "switchboard_websocket_connections", + Name: "armature_websocket_connections", Help: "Current number of active WebSocket connections.", }) @@ -34,17 +34,17 @@ var WebSocketConnections = promauto.NewGauge(prometheus.GaugeOpts{ var ( CompletionTokensTotal = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "switchboard_completion_tokens_total", + Name: "armature_completion_tokens_total", Help: "Total tokens processed by direction (prompt|completion) and model.", }, []string{"direction", "model_id"}) CompletionsTotal = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "switchboard_completions_total", + Name: "armature_completions_total", Help: "Total completion requests by provider, model, and status.", }, []string{"provider_config_id", "model_id", "status"}) CompletionDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "switchboard_completion_duration_seconds", + Name: "armature_completion_duration_seconds", Help: "Completion request latency (wall time including tool loops).", Buckets: []float64{0.1, 0.5, 1, 2, 5, 10, 30, 60, 120}, }, []string{"provider_config_id", "model_id"}) @@ -53,7 +53,7 @@ var ( // ── Provider Health ───────────────────────── var ProviderStatus = promauto.NewGaugeVec(prometheus.GaugeOpts{ - Name: "switchboard_provider_status", + Name: "armature_provider_status", Help: "Provider health status: 0=unknown, 1=healthy, 2=degraded, 3=down.", }, []string{"provider_config_id"}) @@ -61,23 +61,23 @@ var ProviderStatus = promauto.NewGaugeVec(prometheus.GaugeOpts{ var ( DBOpenConnections = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "switchboard_db_open_connections", + Name: "armature_db_open_connections", Help: "Number of open database connections.", }) DBInUseConnections = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "switchboard_db_in_use_connections", + Name: "armature_db_in_use_connections", Help: "Number of database connections currently in use.", }) DBIdleConnections = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "switchboard_db_idle_connections", + Name: "armature_db_idle_connections", Help: "Number of idle database connections.", }) DBWaitCount = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "switchboard_db_wait_count_total", + Name: "armature_db_wait_count_total", Help: "Total number of connections waited for.", }) DBWaitDuration = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "switchboard_db_wait_duration_seconds_total", + Name: "armature_db_wait_duration_seconds_total", Help: "Total time blocked waiting for a new connection (seconds).", }) ) @@ -85,13 +85,13 @@ var ( // ── Task Scheduler Metrics ────────────────── var TaskExecutionsTotal = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "switchboard_task_executions_total", + Name: "armature_task_executions_total", Help: "Total task executions by status (success|error).", }, []string{"status"}) // ── Sandbox Metrics ───────────────────────── var SandboxExecutionsTotal = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "switchboard_sandbox_executions_total", + Name: "armature_sandbox_executions_total", Help: "Total Starlark sandbox executions by entry point and status.", }, []string{"entry_point", "status"}) diff --git a/server/middleware/admin.go b/server/middleware/admin.go index 60b4793..85a0de2 100644 --- a/server/middleware/admin.go +++ b/server/middleware/admin.go @@ -5,8 +5,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/auth" - "switchboard-core/store" + "armature/auth" + "armature/store" ) // RequireAdmin returns middleware that restricts access to users with the diff --git a/server/middleware/auth.go b/server/middleware/auth.go index d98cd6c..8891f16 100644 --- a/server/middleware/auth.go +++ b/server/middleware/auth.go @@ -11,9 +11,9 @@ import ( "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" - "switchboard-core/config" - "switchboard-core/database" - "switchboard-core/store" + "armature/config" + "armature/database" + "armature/store" ) // Claims represents the JWT payload. Must match handlers.Claims. diff --git a/server/middleware/page_auth.go b/server/middleware/page_auth.go index e3e442b..30bd339 100644 --- a/server/middleware/page_auth.go +++ b/server/middleware/page_auth.go @@ -7,10 +7,10 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/auth" - "switchboard-core/config" - "switchboard-core/database" - "switchboard-core/store" + "armature/auth" + "armature/config" + "armature/database" + "armature/store" ) // AuthOrRedirect validates JWT tokens for page routes. diff --git a/server/middleware/permissions.go b/server/middleware/permissions.go index 1d151f8..ae976f7 100644 --- a/server/middleware/permissions.go +++ b/server/middleware/permissions.go @@ -5,8 +5,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/auth" - "switchboard-core/store" + "armature/auth" + "armature/store" ) const permCacheKey = "resolved_permissions" diff --git a/server/middleware/prometheus.go b/server/middleware/prometheus.go index 7b266b1..21b3988 100644 --- a/server/middleware/prometheus.go +++ b/server/middleware/prometheus.go @@ -6,7 +6,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/metrics" + "armature/metrics" ) // Prometheus returns a Gin middleware that records HTTP request metrics. diff --git a/server/middleware/ratelimit.go b/server/middleware/ratelimit.go index 7c6f6b4..a3cf1fd 100644 --- a/server/middleware/ratelimit.go +++ b/server/middleware/ratelimit.go @@ -6,7 +6,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/store" + "armature/store" ) // RateLimiter implements per-IP rate limiting backed by a shared store. diff --git a/server/middleware/team.go b/server/middleware/team.go index eee502f..14de5c6 100644 --- a/server/middleware/team.go +++ b/server/middleware/team.go @@ -5,7 +5,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/store" + "armature/store" ) // RequireTeamAdmin returns middleware that restricts access to team admins. diff --git a/server/notifications/email.go b/server/notifications/email.go index b0b125d..c5d6193 100644 --- a/server/notifications/email.go +++ b/server/notifications/email.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "switchboard-core/store" + "armature/store" ) // SMTPConfig holds SMTP connection settings, loaded from platform_settings. diff --git a/server/notifications/service.go b/server/notifications/service.go index 1fa12b2..5ac352d 100644 --- a/server/notifications/service.go +++ b/server/notifications/service.go @@ -7,9 +7,9 @@ import ( "sync" "time" - "switchboard-core/events" - "switchboard-core/models" - "switchboard-core/store" + "armature/events" + "armature/models" + "armature/store" ) // ── Package-level singleton ───────────────── @@ -53,7 +53,7 @@ func NewService(s store.NotificationStore, hub *events.Hub) *Service { return &Service{ store: s, hub: hub, - instanceName: "Switchboard Core", + instanceName: "Armature", retentionDays: 90, stopCleanup: make(chan struct{}), } diff --git a/server/notifications/service_test.go b/server/notifications/service_test.go index 84178de..22475ed 100644 --- a/server/notifications/service_test.go +++ b/server/notifications/service_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "switchboard-core/models" + "armature/models" ) // mockPrefStore implements store.NotificationPreferenceStore for testing. diff --git a/server/notifications/sources.go b/server/notifications/sources.go index 22cc782..627dd52 100644 --- a/server/notifications/sources.go +++ b/server/notifications/sources.go @@ -6,10 +6,10 @@ import ( "fmt" "log" - "switchboard-core/auth" - "switchboard-core/events" - "switchboard-core/models" - "switchboard-core/store" + "armature/auth" + "armature/events" + "armature/models" + "armature/store" ) // ── Role Fallback ─────────────────────────── diff --git a/server/notifications/sources_test.go b/server/notifications/sources_test.go index f007e95..4e1c385 100644 --- a/server/notifications/sources_test.go +++ b/server/notifications/sources_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "switchboard-core/models" + "armature/models" ) // mockNotifStore implements store.NotificationStore for testing. diff --git a/server/notifications/templates.go b/server/notifications/templates.go index 2b2d104..66a25f7 100644 --- a/server/notifications/templates.go +++ b/server/notifications/templates.go @@ -5,7 +5,7 @@ import ( "html/template" "strings" - "switchboard-core/models" + "armature/models" ) // ── Template Data ─────────────────────────── @@ -54,7 +54,7 @@ Adjust your notification preferences in Settings. // RenderHTML renders an HTML email body for a notification. func RenderHTML(n *models.Notification, instanceName string) string { if instanceName == "" { - instanceName = "Switchboard Core" + instanceName = "Armature" } data := emailData{ Title: n.Title, @@ -73,7 +73,7 @@ func RenderHTML(n *models.Notification, instanceName string) string { // RenderText renders a plaintext email body for a notification. func RenderText(n *models.Notification, instanceName string) string { if instanceName == "" { - instanceName = "Switchboard Core" + instanceName = "Armature" } data := emailData{ Title: n.Title, @@ -92,7 +92,7 @@ func RenderText(n *models.Notification, instanceName string) string { // SubjectForNotification returns an email subject line. func SubjectForNotification(n *models.Notification, instanceName string) string { if instanceName == "" { - instanceName = "Switchboard Core" + instanceName = "Armature" } prefix := "[" + instanceName + "] " switch { diff --git a/server/notifications/templates_test.go b/server/notifications/templates_test.go index 7c4adba..dd102dc 100644 --- a/server/notifications/templates_test.go +++ b/server/notifications/templates_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "switchboard-core/models" + "armature/models" ) func TestRenderHTML_Basic(t *testing.T) { @@ -28,7 +28,7 @@ func TestRenderHTML_Basic(t *testing.T) { func TestRenderHTML_DefaultInstanceName(t *testing.T) { n := &models.Notification{Title: "Test"} html := RenderHTML(n, "") - if !strings.Contains(html, "Switchboard Core") { + if !strings.Contains(html, "Armature") { t.Error("expected default instance name") } } diff --git a/server/pages/loaders.go b/server/pages/loaders.go index 1793c26..d64bcc6 100644 --- a/server/pages/loaders.go +++ b/server/pages/loaders.go @@ -6,7 +6,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/store" + "armature/store" ) // TeamOption for template dropdowns. diff --git a/server/pages/pages.go b/server/pages/pages.go index 79fb6c8..75641d2 100644 --- a/server/pages/pages.go +++ b/server/pages/pages.go @@ -24,8 +24,8 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/config" - "switchboard-core/store" + "armature/config" + "armature/store" ) //go:embed templates/*.html templates/components/*.html templates/surfaces/*.html @@ -867,7 +867,7 @@ func (e *Engine) loadFooter() FooterConfig { // loadBranding reads instance branding from global settings. func (e *Engine) loadBranding() (name, logoURL, tagline string) { - name = "Switchboard Core" // default + name = "Armature" // default if e.stores.GlobalConfig == nil { return } diff --git a/server/pages/pages_surfaces.go b/server/pages/pages_surfaces.go index 1fafa92..f156564 100644 --- a/server/pages/pages_surfaces.go +++ b/server/pages/pages_surfaces.go @@ -7,7 +7,7 @@ import ( "github.com/gin-gonic/gin" - "switchboard-core/middleware" + "armature/middleware" ) // SeedSurfaces writes core surface manifests to the registry table. diff --git a/server/pages/templates/base.html b/server/pages/templates/base.html index 3f04739..83e150b 100644 --- a/server/pages/templates/base.html +++ b/server/pages/templates/base.html @@ -4,7 +4,7 @@ - {{block "title" .}}Switchboard Core{{end}} + {{block "title" .}}Armature{{end}} {{/* Decomposed from monolithic styles.css (v0.22.9) */}} diff --git a/server/pages/templates/login.html b/server/pages/templates/login.html index cfe72ae..c375981 100644 --- a/server/pages/templates/login.html +++ b/server/pages/templates/login.html @@ -4,7 +4,7 @@ - Switchboard Core + Armature diff --git a/server/sandbox/connections_module.go b/server/sandbox/connections_module.go index ccf512c..de3c8d4 100644 --- a/server/sandbox/connections_module.go +++ b/server/sandbox/connections_module.go @@ -18,7 +18,7 @@ import ( "go.starlark.net/starlark" "go.starlark.net/starlarkstruct" - "switchboard-core/models" + "armature/models" ) // ConnectionResolver resolves extension connections with secret decryption. diff --git a/server/sandbox/http_module.go b/server/sandbox/http_module.go index 1b2a7f8..fd2953e 100644 --- a/server/sandbox/http_module.go +++ b/server/sandbox/http_module.go @@ -273,7 +273,7 @@ func executeHTTPRequest( } // Set headers (user-provided override defaults) - req.Header.Set("User-Agent", "ChatSwitchboard-Extension/1.0") + req.Header.Set("User-Agent", "Armature-Extension/1.0") for k, v := range headers { req.Header.Set(k, v) } diff --git a/server/sandbox/lib_module.go b/server/sandbox/lib_module.go index 787175e..cb50c6b 100644 --- a/server/sandbox/lib_module.go +++ b/server/sandbox/lib_module.go @@ -17,7 +17,7 @@ import ( "go.starlark.net/starlark" "go.starlark.net/starlarkstruct" - "switchboard-core/models" + "armature/models" ) // libContext carries per-invocation state for lib.require() calls. diff --git a/server/sandbox/modules.go b/server/sandbox/modules.go index 644c4a7..b59d0b6 100644 --- a/server/sandbox/modules.go +++ b/server/sandbox/modules.go @@ -11,8 +11,8 @@ import ( "go.starlark.net/starlark" "go.starlark.net/starlarkstruct" - "switchboard-core/models" - "switchboard-core/store" + "armature/models" + "armature/store" ) // ─── Secrets Module ────────────────────────── diff --git a/server/sandbox/realtime_module.go b/server/sandbox/realtime_module.go index 6e8c5a8..b32aa9c 100644 --- a/server/sandbox/realtime_module.go +++ b/server/sandbox/realtime_module.go @@ -21,7 +21,7 @@ import ( "go.starlark.net/starlark" "go.starlark.net/starlarkstruct" - "switchboard-core/events" + "armature/events" ) // maxRealtimePayload is the payload size limit (pg_notify safe). diff --git a/server/sandbox/realtime_module_test.go b/server/sandbox/realtime_module_test.go index c770f4f..6b40131 100644 --- a/server/sandbox/realtime_module_test.go +++ b/server/sandbox/realtime_module_test.go @@ -9,7 +9,7 @@ import ( "go.starlark.net/starlark" - "switchboard-core/events" + "armature/events" ) func TestRealtimePublish_HappyPath(t *testing.T) { diff --git a/server/sandbox/runner.go b/server/sandbox/runner.go index 08dfd25..a69bfee 100644 --- a/server/sandbox/runner.go +++ b/server/sandbox/runner.go @@ -30,10 +30,10 @@ import ( starlarkjson "go.starlark.net/lib/json" - "switchboard-core/events" - "switchboard-core/metrics" - "switchboard-core/models" - "switchboard-core/store" + "armature/events" + "armature/metrics" + "armature/models" + "armature/store" ) // sandboxStats tracks cumulative execution counters for the admin metrics endpoint. diff --git a/server/sandbox/settings_module.go b/server/sandbox/settings_module.go index e3eb981..62b3df5 100644 --- a/server/sandbox/settings_module.go +++ b/server/sandbox/settings_module.go @@ -18,7 +18,7 @@ import ( "go.starlark.net/starlark" "go.starlark.net/starlarkstruct" - "switchboard-core/store" + "armature/store" ) // BuildSettingsModule creates the "settings" Starlark module for a package. diff --git a/server/sandbox/workflow_module.go b/server/sandbox/workflow_module.go index 376b26c..6f1fa93 100644 --- a/server/sandbox/workflow_module.go +++ b/server/sandbox/workflow_module.go @@ -21,7 +21,7 @@ import ( "go.starlark.net/starlark" "go.starlark.net/starlarkstruct" - "switchboard-core/store" + "armature/store" ) // BuildWorkflowModule creates the "workflow" Starlark module for a package. diff --git a/server/static/openapi.yaml b/server/static/openapi.yaml index e4181d2..730496f 100644 --- a/server/static/openapi.yaml +++ b/server/static/openapi.yaml @@ -1,6 +1,6 @@ openapi: 3.0.3 info: - title: Switchboard Core API + title: Armature API description: | Self-hosted extension platform. Identity, teams, permissions, workflows, and a package system. Everything else ships as installable extensions. @@ -4820,7 +4820,7 @@ paths: /api/v1/hooks/{package_id}/{slug}: post: summary: Inbound webhook trigger - description: Public endpoint. HMAC-SHA256 verified via X-Switchboard-Signature header. + description: Public endpoint. HMAC-SHA256 verified via X-Armature-Signature header. operationId: webhookInbound tags: [Triggers] security: [] diff --git a/server/static/swagger.html b/server/static/swagger.html index b4795c3..f7bcefe 100644 --- a/server/static/swagger.html +++ b/server/static/swagger.html @@ -4,7 +4,7 @@ - Switchboard Core — API Docs + Armature — API Docs