Skip to main content
Status: DRAFT for founder review · 2026-08-21 Origin: founder direction (2026-08-21, this session): “vexa api here has ticketing system sink, and we need to design the ticketing system itself here” · “they use the ticketing system for first class support” (in the context of reproducible per-provider installs — bring your own AWS, run this command).

What it is

The support surface of the enterprise delivery product. A BYOC customer’s platform team runs our software inside their governance; when something needs us — an install refusal, an admission refusal, a health rollback, a platform change breaking capture, a question — the path is a ticket bound to delivery evidence, not a call and a pastebin. First-class support means the ticket carries the machine state needed to act on it, and its resolution is itself a delivery: most tickets close as fixed_in_entry(N) — a channel entry the customer’s cluster pulls.

Placement — what runs where

Blocking egress entirely degrades nothing (R5): ticketing is advisory-lane; an air-gapped customer files by any out-of-band means and the evidence bundle attaches as files.

Ticket object — v1 draft

Data minimization is load-bearing: payloads carry configuration shapes (taints, LimitRange values, policy names, digests, error strings) and never meeting content, transcripts, names, or identifiers — same posture as the R5 metrics allowlist, and the vexa-support packager shows the operator the full payload before anything is sent.

The loop that makes it first-class

  1. Machine event in their cluster (preflight FAIL, Kyverno refusal, health-judge rollback, break-glass request) → operator runs vexa-support package <event> → reviews → sends to the sink. Severity and kind are pre-classified by the event type.
  2. Our side triages with the evidence already attached — the preflight report names the failing check and the §6 failure class; the refusal names the policy and the digest.
  3. Resolution rides the conveyor: a fix ships as a channel entry; the ticket flips to fixed_in_entry(N); the customer’s staging pulls it, their gate promotes it, the ticket closes on their confirmation (or their cluster’s next successful sync of ≥ N, where they opt into reporting it).
  4. Fleet-relevant causes (a platform changed under everyone) additionally publish a signed advisory on the channel — subscribers see it before they hit it.
break_glass_request is a first-class kind: the request for an incomplete-chain publication arrives as a sev1 ticket, and the approval produces the break_glass record inside the signed entry (ADR-0002 §5) — the audit trail is the ticket + the entry, never a side channel.

SLA surface (commitment shape)

Severity binds to delivery latency, because with a pull conveyor our release latency is the customer’s incident path (Attestation-Conveyor): sev1 = hotfix conveyor cadence (same stations, shortened soak — never a bypass), sev2 = next scheduled entry, sev3 = advisory/answer. Actual numbers are set per subscription, not here.

Automated events — founder direction 2026-08-21 (same session)

The pipeline must also let the deployment call the sink itself for a set of predefined events — entirely optional, off by default, every event class individually enabled by the customer: vendor-bound bandwidth fully under their control.
  • Event classes (initial set): health_degraded · sync_failed · prod_promotion (their pin moved and synced — the promotion notification) · admission_refusal · incident_report (composed bundle) · usage_report (metering, below).
  • Mechanism — no new resident software: Argo CD’s stock notifications engine (triggers + templates, configuration we ship) posts to the sink with the subscription credential; Kyverno policy reports feed refusal events. This preserves the kit’s claim verbatim: we ship configuration, not an agent.
  • Contract: same shapes-not-content payloads; every class off by default; blocking all of it degrades nothing (advisory lane, R5-consistent).

Behind the sink — the vendor-side architecture (founder question 2026-08-21: “what’s behind the sink?”)

The sink itself is deliberately thin; the machinery behind it reuses what the company already runs. Nothing here is a new helpdesk product.
Decisions this encodes (all reuse, no invention):
  1. Two lanes behind one sink. Events (high-volume, no human) and tickets (action-needed) share ingest and schema but not workflow; promotion rules are the only bridge. This is what makes “tickets might be health callbacks built automatically” true without drowning support in telemetry.
  2. GitHub Issues are the workflow, not a copy of it. The bridge means a sev1 platform_change ticket is a scope item the release train can pick up, and fixed_in_entry(N) closure falls out of the existing close-with-release discipline rather than being built.
  3. The store is boring: the vendor-side DB + object storage that already exist; evidence blobs stored by digest exactly like bundle members.
  4. Which vexa API service hosts the thin ingest remains the named open question below (admin-api is the natural candidate); everything behind it is service-agnostic.

The customer-facing lane — email confirmation or private repo? (founder question 2026-08-21)

Both patterns exist in industry; they serve different organizations. The resolution is to keep three surfaces distinct and make the human lane per-subscription configuration:
  1. The internal workflow repo is never customer-visible. One private repo, internal-only, mirroring the ticket store — where the release train picks work up. Customers hold no access of any kind, which removes the cross-tenant-leak class by construction. (One shared repo with customer access is the one option that is simply wrong — never.)
  2. Default customer lane: email, bound to the ticket. On ingest the customer contact gets a confirmation carrying the ticket id; every state change (acknowledged, fixed_in_entry(N), closed) goes out on the same thread; replies to tickets+<id>@ thread back into the ticket. Why default: email is the universal ITSM adapter — ServiceNow and Jira ingest it natively, so the customer’s own ticket system stays in sync without any integration project; change boards and on-call rotations live there; zero identity or licensing friction; works for the most compliance-bound subscriber.
  3. Offered lane for engineer-led subscriptions: a per-customer private repo (support-<subscription>), outside-collaborator access only, with the sync bot mirroring ticket state and conversation both ways. The reference enterprise engagement arrived through a GitHub issue — engineer-champions live there, and for them issues ARE the natural support object. Two disciplines make it safe: evidence never leaves the store (issues carry digests and pointers, not blobs — sensitive payloads stay off GitHub, which also keeps the data-residency question tractable), and access is per-repo outside collaborators, never org membership. Cost note: outside collaborators on private repos consume paid seats — a small per-customer line.
  4. The machine API stays canonical regardless of lane: the sink ingests, the status API answers, and both human lanes are projections of the same ticket store.
Build order: internal repo bridge + outbound email first; inbound email (the ITSM bridge) second; per-customer repos third — config-light once the sync exists. Prerequisite (founder): a transactional sender (tickets@vexa.ai) set up properly; and the residency decision may exclude the GitHub lane for some subscriptions — the lane being per-subscription configuration is exactly the accommodation for that.

Email lane — implementation sketch (and the ownership boundary)

Ownership first (founder observation 2026-08-21: “that’s largely outside the delivery product” — correct). This repo owns the contract: the ticket schema, the sink API shape, the kit’s emitter configuration, and the fixed_in_entry coupling to channel entries. The implementation — sink service, event log, ticket store, promotion rules, email bridge, GitHub bridge, status API — is vendor API-plane machinery and belongs with the vendor-side services (vexa-platform’s proprietary services are the precedent home), tracked as an issue there when the founder says go. Nothing in M3 depends on it. Outbound (build first):
  • A dedicated transactional email provider (Postmark/SES-class), never Workspace Gmail — deliverability, suppression handling and volume are the provider’s job. Sender tickets@vexa.ai; SPF/DKIM/DMARC on a dedicated return-path subdomain via our DNS. Provider signup + DNS changes are founder-gated ops (already flagged as the prerequisite).
  • Threading is the feature: first mail sets a stable Message-ID; every state change replies with In-Reply-To/References plus a [VEXA-<id>] subject token. That is exactly what makes Gmail, Outlook, ServiceNow and Jira keep the whole ticket as one thread — the ITSM bridge is the threading discipline, not an integration.
  • Plain-text templates: confirmation (ticket id + what was received), state changes (acknowledged, fixed_in_entry(N) with the release ref, closed).
Inbound (build second):
  • tickets+<id>@vexa.ai plus-addressing; MX/inbound-parse at the provider posting a webhook to the support service. Acceptance requires both the plus-address ticket id and a DKIM/SPF-passing sender that matches the ticket’s contacts — the id alone is a weak capability, the pair is sturdy. Attachments are stored as evidence blobs by digest with size caps; loop hygiene (Auto-Submitted, suppression, never reply to bounces).
One writer per surface (the house invariant, applied): the ticket store is the single system of record; email and GitHub-issue comments are projections of it, and inbound from either lane lands in the store first, then projects outward. The sync bot is one process owning both mirrors — never two writers on one surface. Effort shape: outbound ≈ provider account + DNS + a day of service code; inbound ≈ two to three days including auth hygiene. Neither blocks anything in the delivery product.

The two-way evidence channel — founder reframe 2026-08-21

“Validation evidence channel. We send our validations, they send theirs back to us (support API).” This names what the event lanes above actually are: the upstream half of the channel.
  • Down: our evidence — staging validation, prod soak, provenance, signatures.
  • Up (opt-in, per class, their switch): their evidence — validation_report (their staging/gate verdicts; the kit’s approve.sh already produces the payload: release, verdict, verdict hash, approver, entry digest), plus the existing classes (health, sync, promotion, refusals, usage).
Why it compounds: release N+1 can ship with evidence nobody else can mint OR aggregate — “validated in Vexa’s prod and in K subscriber stagings across their providers.” Every subscriber strengthens the promise for all — a network effect on evidence. Honesty rule: fleet evidence enters entries as its own kind, signed by the reporting environment’s identity (ADR-0004’s N-identities), never blended into our receipts. A customer who sends nothing back still receives the full down-channel; they simply do not strengthen it.

Metering

Industry practice, surveyed:
  • Industry practice for BYOC/self-managed is self-reported usage under contract, with true-up/audit clauses — GitLab self-managed usage ping plus an offline usage file for air-gapped renewals; Atlassian Data Center self-declared user tiers; Red Hat self-declared subscriptions; cloud-marketplace metered billing as the procurement-friendly variant later. Structurally, in BYOC the vendor cannot measure independently — the pipe is convenience and evidence; the contract is the enforcement (pipe off ⇒ manual report per contract; the product itself is never degraded).
  • The shape that fits us: per-cluster channel subscription as the floor (the access product) plus one agreed usage dimension as the scaler (e.g., active accounts / activation events), reported through this pipe on an agreed cadence.
  • Two requirements before charging on it:
    1. A sealed definition of the metric — one definition, one carrier, queryable by the customer themselves so both sides compute the same number from the same source. (House lesson: an ambiguously-defined metric poisons every conversation it enters; the metering dimension gets a sealed schema’d definition or it does not get billed on.)
    2. Signed usage reports — the report is a signed statement from the customer’s side, using the same attestation machinery the delivery already carries. True-ups then compare receipts, not recollections. Nothing standard in the industry does this; for us it is a small extension and turns billing disputes into hash comparisons.
  • Open (founder, Q3): the dimension itself, price levels, cadence, and whether the marketplace route (AWS/Azure/GCP metered private offers) is worth its integration cost. Nothing metering-related enters the customer docs until Q3 is decided — a documented metric is a published term.

Open questions (founder)

  1. Which vexa API service hosts the sink (and its auth = the subscription credential?). The sink contract will be filed as an issue on the owning repo once named — customer-visible, so founder-gated.
  2. Portal or API-only at v1? API-only + email bridge is the cheapest honest start.
  3. Retention / data residency for ticket evidence (regulated buyers will ask).
  4. Tier mapping — which response/delivery SLAs at which subscription tier (couples to §9 Q3).

What this is not

Not a general helpdesk product, not a status page, not telemetry (R5 metrics stay separate), and not a resident agent in the customer’s cluster. The kit ships a packager and configuration; the system of record is vendor-side.