feat(canary): token HTTP handlers (API + trigger routes)
Phase 9 task 9.6.
Routes:
RegisterAPIRoutes(r):
GET /tokens/types → static list of 7 type descriptors
POST /tokens → CreateToken (validation + service.Create)
RegisterTriggerRoutes(r):
GET /c/{id} → HandleTrigger (webbug, slowredirect,
docx, pdf, envfile use this path)
POST /c/{id}/fingerprint → HandleFingerprint (slowredirect)
HandleFunc /k/{id} → HandleTrigger (kubeconfig, bare path)
HandleFunc /k/{id}/* → HandleTrigger (kubeconfig wildcard,
any HTTP method)
HandleTrigger flow:
- extract token id from chi.URLParam
- service.GetByID (returns nil-nil for not-found)
- if token exists and !enabled, treat as unknown
- resolve generator (known token → its type; unknown + path
prefix /k/ → kubeconfig; unknown otherwise → webbug)
- generator.Trigger(ctx, tok, r) → event + response
- record event via EventRecorder interface if present (Phase 10
wires real event.Service)
- write response: headers + body OR 302 + Location for redirect
CreateToken flow:
- 64KB body cap via http.MaxBytesReader
- JSON decode (bad → 400 BAD_JSON)
- extract fingerprint + real IP via middleware helpers
- service.Create
- error switch: validation/unknown-type/destination/include-keys
map to 400; generator-failure to 500
- happy path: 201 with {token, artifact} envelope per spec §8.2
ArtifactJSON discriminator pattern:
- kind=url → url + destination_url
- kind=file → filename + content_type + content_b64 (base64)
- kind=text → filename + content_type + content (raw)
- kind=conn... → connection_string
Tests (~10 cases): 7-type list, happy path with envelope assertion,
bad JSON, validation failure, known-token records event + writes
response, unknown-token still returns shape (defense-in-depth),
disabled-token treated as unknown, fingerprint without recorder
returns 204, ArtifactJSON discriminator for all 4 kinds.
Phase 9 task 9.6 discharged. Wire-up + config + webbug refactor +
integration test next.