# go/lib — Shared Go library for the Keenan Vision service mesh `go/lib` (module `code.tnxs.net/vernonkeenan/lib`) is the shared toolchain every Go microservice in the Keenan Vision mesh pins in its `go.mod`. It is not a service — it ships no server, no Dockerfile, no compose entry — it is a library `app` package (`InitConfig`/`InitDB`, config accessors, zap logging, Prometheus/CORS middleware, generic marshal helpers) plus a set of go-swagger–generated typed HTTP clients (`api/`) against the sibling services' own swagger specs. Because every mesh service pins the same module, a `lib` bump is a constellation-wide rebuild (KV-C6a). ## Constellation context - Toolchain member of the Keenan Vision Go mesh governed by `../../kv-meta/` (see `../../kv-meta/CONSTELLATION.md` §1.3, contract KV-C6). Not a deployed service: no swagger server, no port, no entry in the `docker/kvweb` compose on pluto. - KV-C6a: every mesh service (`auth`, `cache`, `crm`, `members`, `pdf`, `plex`, `render`, `research`, `sf-gate`, `stash`) pins an explicit `code.tnxs.net/vernonkeenan/lib` tag in its own `go.mod`; consumption is a per-consumer `go.mod` bump, never automatic. - Distribution is via the world-readable Gitea pull-mirror at `code.tnxs.net/vernonkeenan/lib`, enabling credential-free `go get code.tnxs.net/vernonkeenan/lib@vX.Y.Z` from Docker/CI/dev boxes even though the GitHub repo itself is private (ADR-KV-014). Everything committed here is treated as **published, permanently** — a gitleaks CI gate makes leaking a secret a build failure, not a review nit. - CI (self-hosted runner, `go build/vet/test ./...` + gitleaks on every push/PR) landed 2026-07-12 as `lib`'s first-ever pipeline (ADR-KV-014) — `lib` was the only one of the 12 Go-roster repos with no CI at all going into the 2026-07-12 test-harness audit, so this closes out that gap. The `test` job is `lib`'s instance of the mesh-wide unit-test-gate program (ADR-KV-009, amended by ADR-KV-013, which scopes `lib` as one of its 12 governed repos); the `gitleaks` job is ADR-KV-014's public-mirror secret-scan gate, specific to `lib` because it's the one repo mirrored world-readable. - `api/` also acts as `lib`'s own internal client of the mesh: `app/auth.go`'s `CheckAPIUser` calls `auth`'s generated client to validate an API key, so `lib` is simultaneously a library *and* a consumer of one sibling service. ## Package surface | Path | What it is | |---|---| | `app/root.go` | `InitConfig(systemName, level)` — loads `/etc/vernonkeenan/.yaml` via viper, one-time init guard | | `app/config.go` | `Configuration` struct + `Get*`/`Is*` accessors (DBMS, DSN, authentication mode, log level, metrics, service accounts, workers, email, chunk/cache sizing) | | `app/mysql.go` | `InitDB()` — opens `MyDB *sql.DB` against the configured MySQL DSN | | `app/auth.go` | `CheckAPIUser(token)` — validates a credential against `auth`; errors never log the raw value | | `app/prometheus.go` | `SetupPrometheusHandler` — CORS + request-count/duration middleware | | `app/service-helpers.go` | Pointer helpers (`String`/`Bool`/`Int64`/…), `GetFieldsAndValues` reflection helper (unit-tested) | | `app/user.go`, `user-helpers.go`, `tenantuser.go`, `userrole.go`, `address.go` | Compatibility principal/domain structs. ADR-KV-024 adds `PrincipalKey`, `PrincipalType`, exact `Scopes`, `HasScope`, and `IsServicePrincipal`; no native response carries its bearer secret. | | `app/logger/logger.go` | Thin `zap.SugaredLogger` wrapper (`New(level)`, level constants) | | `api/{auth,crm,members,plex,research,sfgate,stash}/` | go-swagger–generated typed clients + models, one directory per sibling service, regenerated from that service's own spec | | `swagger/*.yaml`, `swagger/defs/`, `swagger/external/` | Copies of each sibling service's spec (the KV-C6b spec-copy problem — no sync mechanism besides `make regen`), shared component defs, and host/path-rewritten copies for external doc publishing; also carries legacy vendored Kazoo/Clerk specs from `lib`'s pre-mesh history | ## Build, run, test ```bash go build ./... go vet ./... go test ./... # table-driven unit tests today: app/service-helpers.go pure helpers make install-swagger # install the pinned go-swagger CLI (SWAGGER_VERSION in Makefile) make regen # aka `make swagger` — regenerate api/ from sibling specs make release VERSION=vX.Y.Z make release-dry-run VERSION=vX.Y.Z ``` `go test ./...` is CI-gating on every push and PR (ADR-KV-009/013), same as the rest of the mesh; `lib` is a library so there is nothing to "run." `make regen` requires the sibling repos `auth`, `crm`, `stash`, `sf-gate`, `research`, `members`, `plex` cloned adjacently — it reads each one's `swagger/-vernonkeenan.yaml`, copies it into `./swagger` and `./swagger/external`, and regenerates every client under `api/`, rewriting the external specs' scheme/host/path the same way the old `make swagger` ritual did. It fails fast, before touching anything, if any sibling spec is missing. Review `git status`/`git diff`, commit, open a PR, and merge it to `main` like any other change — client regen is never bundled into the release step; `make regen` runs a non-fatal `go build ./...` at the end as an early warning, but the real gate is CI on that PR. **Known gap (resolved for the current spec set, but can recur):** if any sibling spec has an operation with no top-level `tags:`, go-swagger routes it into a fallback `operations` package whose import path in `_client/_client.go` doesn't respect `--client-package` — reproduced across go-swagger v0.33.2 through v0.35.0 while building this tooling. The one instance found, `sf-gate-vernonkeenan.yaml`'s `PUT /researchprojectservices` (no `tags:`), was fixed at the source (sf-gate #3 added the missing `tags:` entry) and regenerated clean in the v0.7.5 `feat/regen-clients-processor-token` PR — `api/sfgate` has no fallback `operations` package today. If a future sibling spec change reintroduces an untagged operation, `make regen`'s non-fatal `go build ./...` step will fail with this same signature; fix it at the source (add a `tags:` entry in that service's spec) rather than hand-patching generated code, since the next regen would just reintroduce it. ## Configuration `lib` reads no config file of its own. `app.InitConfig(systemName, level)` loads config **on behalf of the calling service**: viper reads `/etc/vernonkeenan/.yaml` (YAML), with environment-variable overrides via prefix `VERNONKEENAN_` (`.` replaced by `_` in key names), unmarshalled into `app.Configuration` — `AppName`, `BackendID`, `DBMS`/`DBMSHost`/`DBMSName`/`DBMSUsername`/`DBMSPassword`, `DSN`, `Environment`, `LogLevel`, `GelfURI`, `Metrics` (address/enabled), `ServiceAccounts` (a map of external-system credentials, e.g. the `auth` service account `CheckAPIUser` reads its own API key from), `Workers`, `Email`, `CacheSizes`/`Chunks`. The concrete `/etc/vernonkeenan/.yaml` file lives on each deploy host, not in this repo — see the calling service's own README for its file name and required keys. ## Versioning & pinning contract (KV-C6a) - **Module:** `code.tnxs.net/vernonkeenan/lib`, fetched via the Gitea anonymous mirror (not GitHub directly — the GitHub repo is private). - **Pin, don't float:** every consumer pins an explicit `vX.Y.Z` tag in its `go.mod`. Bumping the pin is a normal PR in the consumer repo; there is no auto-bump mechanism. - **Release is one command:** `make release VERSION=vX.Y.Z` (`scripts/release.sh`) from a clean `main` checkout that matches `origin/main`: 1. Validates `VERSION` is `vMAJOR.MINOR.PATCH` and strictly greater than the latest existing tag. 2. Requires you're on `main`, the tree is clean, and local `main` matches `origin/main` (no local-only commits). 3. Runs `go build ./...` and `go vet ./...` as a last sanity check. 4. Creates an annotated tag at HEAD, pushes it, and runs `gh release create` with generated notes. 5. POSTs an explicit zero-length body to the Gitea mirror-sync API for `vernonkeenan/lib` (skipped with a warning if `GITEA_TOKEN` is unset). `make release-dry-run VERSION=vX.Y.Z` runs steps 1-3 and prints what steps 4-5 would do, without tagging, pushing, publishing, or syncing anything. - **A `lib` bump is constellation-wide:** any breaking change to `app.Configuration`, `InitConfig`/`InitDB`, or a generated client's shape ripples to every pinned service on its next `go.mod` bump. v0.7.10 deliberately removes the retired `InitForce` API and `go-force` dependency. `sf-gate` stays pinned to its prior Lib while awaiting retirement; deprecated Cache must not be rebuilt or upgraded. - **Branch-per-version is retired** (ADR-KV-014, 2026-07-12): `lib` moved to main + `v*` tags, replacing the old three-web-UI-plus-CLI manual release ritual. ~70 legacy `vX.Y.Z` branches (one per pre-migration release; `git branch --list 'v*'`) remain in the repo as historical artifacts of the old convention, not the active release mechanism. - **Prerequisites for a release:** the sibling repos (for `make regen`) cloned adjacently; [`gh`](https://cli.github.com/) authenticated with push access to `vernonkeenan/lib`; `GITEA_TOKEN` exported for the mirror-sync step (optional — see above). ## Release & deploy `main` + PR flow: land changes on `main` via PR (CI runs build/vet/test + gitleaks on the PR and on push to `main`), then cut a release with `make release VERSION=vX.Y.Z`, which tags `main`, publishes a GitHub release, and triggers the Gitea mirror-sync. Unlike the deployed mesh services, `lib` has **no Dockerfile, no container image, and no deploy step** — it is never built into `hub.tnxs.net` and never rolled out via the `docker/kvweb` compose on pluto. "Release" means "a new tag other repos can pin," full stop; downstream rollout happens when each consumer bumps its own `go.mod` line for `code.tnxs.net/vernonkeenan/lib` to the new tag and goes through its own release/deploy path.