2026-07-14 19:31:08 +00:00
# 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
2026-07-22 19:48:40 +00:00
a library `app` package (`InitConfig`/`InitDB`, config
2026-07-14 19:31:08 +00:00
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.
2026-07-22 18:15:00 +00:00
- Distribution is via the world-readable Gitea pull-mirror at
`code.tnxs.net/vernonkeenan/lib` , enabling
2026-07-14 19:31:08 +00:00
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/<systemName>.yaml` via viper, one-time init guard |
2026-07-22 18:12:56 +00:00
| `app/config.go` | `Configuration` struct + `Get*` /`Is*` accessors (DBMS, DSN, authentication mode, log level, metrics, service accounts, workers, email, chunk/cache sizing) |
2026-07-14 19:31:08 +00:00
| `app/mysql.go` | `InitDB()` — opens `MyDB *sql.DB` against the configured MySQL DSN |
2026-07-22 18:12:56 +00:00
| `app/auth.go` | `CheckAPIUser(token)` — validates a credential against `auth` ; errors never log the raw value |
2026-07-14 19:31:08 +00:00
| `app/prometheus.go` | `SetupPrometheusHandler` — CORS + request-count/duration middleware |
| `app/service-helpers.go` | Pointer helpers (`String`/`Bool`/`Int64`/…), `GetFieldsAndValues` reflection helper (unit-tested) |
2026-07-22 18:12:56 +00:00
| `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. |
2026-07-14 19:31:08 +00:00
| `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
feat: replace manual release ritual with `make release` (ADR-KV-014)
The README's "Update Procedure" required three web UIs (GitHub branch
defaults, GitHub releases, Gitea "Synchronize Now") and a go-swagger CLI
installed nowhere. Now that lib is main+tags instead of branch-per-version,
replace it with scripts/release.sh driven through the Makefile:
- `make install-swagger` installs a pinned go-swagger (v0.35.0) and
verifies the version.
- `make regen` copies the sibling service specs and regenerates api/
clients exactly as the old `make swagger` ritual did, failing fast if
a sibling spec is missing. Regen output is reviewed/committed as its
own PR, same as any other change.
- `make release VERSION=vX.Y.Z` / `make release-dry-run VERSION=vX.Y.Z`
validate semver + monotonicity, require a clean main checkout synced
with origin/main, tag, push, `gh release create`, and POST to the
Gitea mirror-sync API for the vernonkeenan/lib and work/lib mirrors
(skipped with a loud warning if GITEA_TOKEN is unset).
Also scope the CI push trigger to `main` + `v*` tags instead of every
branch, so PRs (which already run via the `pull_request` trigger) stop
double-running.
Verified locally: bash -n + shellcheck clean; install-swagger into a
temp GOBIN across go-swagger v0.33.2-v0.35.0; semver/monotonicity/
tag-exists/branch/origin-sync validation failure paths; a full regen
against the real sibling specs in a scratch dir (never the checked-out
api/swagger). That regen surfaced a real go-swagger limitation --
documented in the README and as a warning in the script -- reproduced
across all four installable versions: an untagged operation in
sf-gate-vernonkeenan.yaml produces a client that doesn't compile.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 06:49:23 +00:00
```bash
2026-07-14 19:31:08 +00:00
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
feat: replace manual release ritual with `make release` (ADR-KV-014)
The README's "Update Procedure" required three web UIs (GitHub branch
defaults, GitHub releases, Gitea "Synchronize Now") and a go-swagger CLI
installed nowhere. Now that lib is main+tags instead of branch-per-version,
replace it with scripts/release.sh driven through the Makefile:
- `make install-swagger` installs a pinned go-swagger (v0.35.0) and
verifies the version.
- `make regen` copies the sibling service specs and regenerates api/
clients exactly as the old `make swagger` ritual did, failing fast if
a sibling spec is missing. Regen output is reviewed/committed as its
own PR, same as any other change.
- `make release VERSION=vX.Y.Z` / `make release-dry-run VERSION=vX.Y.Z`
validate semver + monotonicity, require a clean main checkout synced
with origin/main, tag, push, `gh release create`, and POST to the
Gitea mirror-sync API for the vernonkeenan/lib and work/lib mirrors
(skipped with a loud warning if GITEA_TOKEN is unset).
Also scope the CI push trigger to `main` + `v*` tags instead of every
branch, so PRs (which already run via the `pull_request` trigger) stop
double-running.
Verified locally: bash -n + shellcheck clean; install-swagger into a
temp GOBIN across go-swagger v0.33.2-v0.35.0; semver/monotonicity/
tag-exists/branch/origin-sync validation failure paths; a full regen
against the real sibling specs in a scratch dir (never the checked-out
api/swagger). That regen surfaced a real go-swagger limitation --
documented in the README and as a warning in the script -- reproduced
across all four installable versions: an untagged operation in
sf-gate-vernonkeenan.yaml produces a client that doesn't compile.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 06:49:23 +00:00
```
2026-07-14 19:31:08 +00:00
`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/<service>-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
`<name>_client/<name>_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
feat: replace manual release ritual with `make release` (ADR-KV-014)
The README's "Update Procedure" required three web UIs (GitHub branch
defaults, GitHub releases, Gitea "Synchronize Now") and a go-swagger CLI
installed nowhere. Now that lib is main+tags instead of branch-per-version,
replace it with scripts/release.sh driven through the Makefile:
- `make install-swagger` installs a pinned go-swagger (v0.35.0) and
verifies the version.
- `make regen` copies the sibling service specs and regenerates api/
clients exactly as the old `make swagger` ritual did, failing fast if
a sibling spec is missing. Regen output is reviewed/committed as its
own PR, same as any other change.
- `make release VERSION=vX.Y.Z` / `make release-dry-run VERSION=vX.Y.Z`
validate semver + monotonicity, require a clean main checkout synced
with origin/main, tag, push, `gh release create`, and POST to the
Gitea mirror-sync API for the vernonkeenan/lib and work/lib mirrors
(skipped with a loud warning if GITEA_TOKEN is unset).
Also scope the CI push trigger to `main` + `v*` tags instead of every
branch, so PRs (which already run via the `pull_request` trigger) stop
double-running.
Verified locally: bash -n + shellcheck clean; install-swagger into a
temp GOBIN across go-swagger v0.33.2-v0.35.0; semver/monotonicity/
tag-exists/branch/origin-sync validation failure paths; a full regen
against the real sibling specs in a scratch dir (never the checked-out
api/swagger). That regen surfaced a real go-swagger limitation --
documented in the README and as a warning in the script -- reproduced
across all four installable versions: an untagged operation in
sf-gate-vernonkeenan.yaml produces a client that doesn't compile.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 06:49:23 +00:00
next regen would just reintroduce it.
2026-07-14 19:31:08 +00:00
## 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/<systemName>.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/<name>.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.
2026-07-22 18:15:00 +00:00
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).
2026-07-14 19:31:08 +00:00
`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
2026-07-22 19:48:40 +00:00
`app.Configuration` , `InitConfig` /`InitDB`, or a generated
2026-07-14 19:31:08 +00:00
client's shape ripples to every pinned service on its next `go.mod` bump.
2026-07-22 19:48:40 +00:00
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.
2026-07-14 19:31:08 +00:00
- **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.