Skip to content

spec(governance): tenant webhook boundary covers event_categories (v0.1.25.38)#126

Merged
amavashev merged 1 commit into
mainfrom
spec/gov-tenant-webhook-category-boundary-v0.1.25.38
Jul 11, 2026
Merged

spec(governance): tenant webhook boundary covers event_categories (v0.1.25.38)#126
amavashev merged 1 commit into
mainfrom
spec/gov-tenant-webhook-category-boundary-v0.1.25.38

Conversation

@amavashev

Copy link
Copy Markdown
Contributor

Summary

Governance spec revision v0.1.25.38: the tenant self-service webhook boundary now normatively covers event_categories, and the stale tenant-webhook prose (counts + admin-only list) is corrected. Prose/documentation only — no schema shapes, operations, or status codes change; semantic base stays 0.1.25.9.

The gap (verified in the reference admin server; fixed in parallel as cycles-server-admin 0.1.25.50)

WebhookTenantController.validateTenantEventTypes rejects admin-only event_types for tenant keys, but event_categories was not validated — and WebhookRepository.matchesEventType treats categories as an additive union with event_types. A tenant subscription pairing one allowed type with event_categories: ["api_key"] therefore received admin-only events. The spec never stated the category half of the boundary, so the server gap was also a spec gap.

Normative text added

createTenantWebhook:

TENANT-ACCESSIBLE BOUNDARY (NORMATIVE):

  • event_types: every entry MUST be tenant-accessible (budget., reservation., tenant.*). A restricted event type MUST be rejected with 400 INVALID_REQUEST.
  • event_categories: every entry MUST likewise be a tenant-accessible category (budget, reservation, tenant). A category whose event types are admin-only (api_key, policy, webhook, system) MUST be rejected with 400 INVALID_REQUEST. Rationale: event_categories is ADDITIVE with event_types in delivery matching (see WebhookSubscription.event_categories) — an unvalidated category widens the subscription beyond the tenant-accessible set even when every entry in event_types is allowed, so validating event_types alone does not enforce the boundary.
  • The same boundary applies to updateTenantWebhook, partial updates included: any provided event_types or event_categories array is validated in full.

updateTenantWebhook (first line): "Restricted to tenant-accessible event types AND event categories: any provided event_types or event_categories array is validated in full against the tenant-accessible boundary … Admin-only entries MUST be rejected with 400 INVALID_REQUEST." Both endpoints' 400 response descriptions now say "restricted event type or event category". WebhookSubscription.event_categories carries a matching schema-level note (the one place documenting the additive matching semantics); the Create/Update request schemas carry no per-field prose by design (shared admin+tenant shapes) — the endpoint prose is the normative home.

Count corrections (recounted against the enum)

The EventType enum has 51 values: budget 17, reservation 6, tenant 6, webhook 7, api_key 7, policy 3, system 5. Tenant-accessible (budget./reservation./tenant.) = 29 of 51 — the prose said "26 of 40" (pre-0.1.25.29 numbers). The admin-only list omitted webhook.* entirely (the category landed at revision 0.1.25.33); it now reads api_key., policy., webhook., system.*. listTenantEvents' boundary sentence was checked and is already correct (no counts).

Empty-both check (requested)

event_types + event_categories both empty (which a naive matcher would treat as match-all) is not reachable through the documented API: event_types carries minItems: 1 on WebhookSubscription, WebhookCreateRequest, and WebhookUpdateRequest, and updates are partial — a provided array must satisfy minItems, an omitted field is unchanged — so event_types can never become empty. No additional normative rule needed; recorded in the changelog.

Also fixed

Stale document-revision self-references: the info.summary headline and SPEC FAMILY CONTEXT were stuck at 0.1.25.36 through the 0.1.25.37 bump — both now read 0.1.25.38 and the summary chronicle gains .37/.38 clauses.

Bookkeeping / validation

  • Changelog entry v0.1.25.38 — 2026-07-10 in changelogs/cycles-governance-admin.md (format precedent).
  • spec-index: base_governance → 0.1.25.38, spec_index → 0.1.37.
  • merged/cycles-openapi-admin-merged.yaml regenerated (byte-stable second run — merge-check passes; source metadata picks up v0.1.25.38 dynamically). Protocol merged artifact unaffected.
  • YAML parse OK; validate_changelogs.py green. Spectral runs in CI.

….1.25.38)

Prompted by a confirmed authorization gap in the reference admin server
(fixed in parallel as cycles-server-admin 0.1.25.50): tenant-plane
webhook validation rejected admin-only event_types but did NOT validate
event_categories, and delivery matching treats categories as an
ADDITIVE union with event_types — so a tenant subscription pairing one
allowed type with event_categories:["api_key"] received admin-only
events. The spec never stated the category half of the boundary, and
its tenant-webhook prose had gone stale.

governance v0.1.25.38:
- createTenantWebhook gains a TENANT-ACCESSIBLE BOUNDARY (NORMATIVE)
  block: every event_categories entry MUST be a tenant-accessible
  category (budget, reservation, tenant); a category whose event types
  are admin-only (api_key, policy, webhook, system) MUST be rejected
  with 400 INVALID_REQUEST — matching the existing event_types rule.
  Rationale stated: categories are additive in matching, so an
  unvalidated category widens the subscription beyond the
  tenant-accessible set even when every event_types entry is allowed.
- updateTenantWebhook: same boundary, partial updates covered
  explicitly (any provided event_types or event_categories array is
  validated in full). Both endpoints' 400 response descriptions gain
  "or event category".
- WebhookSubscription.event_categories description gains the
  tenant-plane boundary note (the one schema-level place that documents
  the additive matching semantics). WebhookCreateRequest /
  WebhookUpdateRequest carry no per-field prose by design (shared
  admin+tenant shapes); the endpoint prose is the normative home.
- Stale counts corrected: the EventType enum has 51 values (budget 17,
  reservation 6, tenant 6, webhook 7, api_key 7, policy 3, system 5);
  tenant-accessible = 29 of 51 (prose said "26 of 40"); the admin-only
  list now includes webhook.* (omitted since the category landed in
  0.1.25.33).
- Empty-both check: event_types + event_categories both empty (naive
  matcher = match-all) is NOT reachable via the documented API —
  event_types has minItems:1 on WebhookSubscription /
  WebhookCreateRequest / WebhookUpdateRequest and updates are partial —
  so no additional rule needed; recorded in the changelog.
- Also fixes stale document-revision self-references (info.summary
  headline + SPEC FAMILY CONTEXT were stuck at 0.1.25.36 through the
  .37 bump) and extends the summary chronicle with .37/.38.

Prose/documentation only — no schema shapes, operations, or status
codes change; semantic base stays 0.1.25.9. Changelog entry added;
spec-index pins bumped (base_governance -> 0.1.25.38, spec_index ->
0.1.37); admin merged artifact regenerated (byte-stable second run;
source metadata picks up v0.1.25.38 dynamically). Validation: YAML
parse OK, validate_changelogs.py green. Spectral runs in CI.
@amavashev amavashev merged commit 8b1cd8f into main Jul 11, 2026
5 checks passed
@amavashev amavashev deleted the spec/gov-tenant-webhook-category-boundary-v0.1.25.38 branch July 11, 2026 00:03
@amavashev amavashev restored the spec/gov-tenant-webhook-category-boundary-v0.1.25.38 branch July 11, 2026 11:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant