Kaiten

Guides

Answer Who Had Access

Turn "who had access to what, when?" into a read of two trails — an Instance's, for the License it ran under and what it consumed, and the organization's, for the grants, the flag rules and the evaluations — with the queries, the cursor walk, and two worked questions from support and from a dispute.

The question arrives from support ("why did this customer see the feature?"), from finance ("was this usage refused?"), or from an auditor ("show me who could do what, and when"). In Kaiten it has one answer surface: the audit trail, where every change to the contract and every evaluation against it is recorded, append-only. It is read at two levels. An Instance's trail holds what happened to that instance. The organization's trail holds everything, including the entries recorded without an Instance. This guide is the query side — which entry answers which question, and where it is read.

You need $KAITEN_API and a token carrying read:instances, as set up in the quickstart — plus read:organizations to read the organization's trail over the API — and the Kaiten Sushi Shop dataset loaded: the demo seed profile, or the Cloud demo sandbox. Responses are illustrative — ids and timestamps depend on your stack.

What to look for

Each entry names its event. The events that answer access questions, and the trail each one is recorded on:

QuestionEventRecorded onThe entry records
Which License did the instance run under, and since when?INSTANCE_CREATED, INSTANCE_UPDATEDThe InstanceThe instance after the change — including its licenseSlug
What did that License grant at the time?LICENSE_ENTITLEMENT_ASSIGNED, LICENSE_ENTITLEMENT_UPDATED, LICENSE_ENTITLEMENT_UNASSIGNEDThe organizationThe grant, with its value and its overage allowance
What did the flag's rules say at the time?FEATURE_FLAG_CREATED, FEATURE_FLAG_UPDATEDThe organizationThe flag after the change — its variants, its default, and its targeting rules in order
What did a flag resolve to?FEATURE_FLAG_EVALUATEDThe organizationThe flag, the variant served, the reason, and the rule that matched — never the caller's context, and not the subject of the evaluation
What did the integration read?ENTITLEMENT_VALUE_GETThe InstanceAn entitlement's resolved value at read time, as returned to the caller
Did this usage count?ENTITLEMENT_USAGE_REPORT_ACCEPTEDThe InstanceThe report folded into the total — value, behavior, resulting event_count
Was this action refused?ENTITLEMENT_USAGE_REPORT_REJECTEDThe InstanceThe report refused above the grant's maximum allowed usage — a hard cap, or the edge of a soft limit's allowance — with the same fields and status: REJECTED
When did the customer hit the cap?INSTANCE_ENTITLEMENT_USAGE_REACHEDThe InstanceThe usage record at the moment it landed on the grant

A filter takes the catalog name — the eventName of each entry, such as INSTANCE_UPDATED — not the dotted eventType. The full list is the event catalog.

Evaluations are recorded for the organization

Kaiten follows the OpenFeature Remote Evaluation Protocol, in which an evaluation is addressed by a targeting key. Its audit entry is recorded for the organization, without an Instance: it appears in the organization's trail and never in an Instance's — neither on the console's per-instance Audit Trail tab nor through GET /instances/{instanceSlug}/audit-trails. Two more properties matter when you rely on it:

  • Best effort. The entry is written after the response has been served. Under sustained load one can be dropped; an evaluation is never slowed down or failed to record it.
  • Not for Kaiten's own flags. The evaluations Kaiten makes of its own flags, for its own organization, are not recorded.

Recording the Instance sent in the evaluation context on the audit entry is in progress Coming soon.

The question "who had access to what, when?" is therefore answered from the history of the contract, which is complete and dated: where a rule reads the contract, a decision follows from the License the instance ran under, the grants of that License, and the rules of the flag. The evaluation entries confirm what was served, and when.

Start from the instance

Access is always some instance's access, and the instance's own trail says which License it ran under, and when.

  1. Open Customers → Instances, then sakura-tokyo-prod, then its Audit Trail tab. The Event Log lists the instance's latest 200 entries, newest first.
  2. Narrow the list with the search box, or filter by entitlement group, by event, or by status. The tab has no time range: bound one on the organization's page, below, or over the API.
  3. Click View details, the eye at the end of a row. The dialog shows the entry's Full Payload: on an Instance created or Instance updated entry, licenseSlug is the License version the instance was given at that moment.
Sakura Tokyo Production's Event Log filtered on Instance created, with the entry's details: the instance's slug and the License it was created under, premium-v2Sakura Tokyo Production's Event Log filtered on Instance created, with the entry's details: the instance's slug and the License it was created under, premium-v2

Narrow by event and time

Three query parameters do the work on an Instance's trail: event_name (exact catalog name), after and before (RFC 3339, inclusive). Every change made to sakura-tokyo-prod over the last thirty days — a change of License among them:

AFTER=$(date -u -d '30 days ago' +%Y-%m-%dT%H:%M:%SZ)   # macOS: date -u -v-30d +%Y-%m-%dT%H:%M:%SZ

curl "$KAITEN_API/instances/sakura-tokyo-prod/audit-trails?event_name=INSTANCE_UPDATED&after=$AFTER" \
  -H "Authorization: Bearer $KAITEN_TOKEN"

One event name per query. To answer a question that spans several events — a report and the cap it hit — run one query per event, or read the unfiltered trail for the time window and sort by eventName locally.

Walk the pages

A page holds up to 200 entries (limit, default 50). hasMore says whether older entries exist; nextCursor fetches them. To hand a complete window to an auditor, walk the cursor to the end and keep every entry — here, the last ninety days:

AFTER=$(date -u -d '90 days ago' +%Y-%m-%dT%H:%M:%SZ)   # macOS: date -u -v-90d +%Y-%m-%dT%H:%M:%SZ

url="$KAITEN_API/instances/sakura-tokyo-prod/audit-trails?after=$AFTER&limit=200"
cursor=""
while :; do
  page=$(curl -s "$url${cursor:+&cursor=$cursor}" -H "Authorization: Bearer $KAITEN_TOKEN")
  echo "$page" | jq -c '.items[]' >> sakura-tokyo-prod-audit.jsonl
  [ "$(echo "$page" | jq -r '.hasMore')" = "true" ] || break
  cursor=$(echo "$page" | jq -r '.nextCursor')
done

The file is one JSON entry per line, newest first, exactly as recorded. The CLI follows the cursor for you and returns the window as one JSON array; its --event-name filter takes the same exact catalog name:

kaiten instances audit-trails sakura-tokyo-prod --event-name INSTANCE_UPDATED --after "$AFTER" --output json

Read the organization's trail

Grants, flag changes and flag evaluations carry no Instance, so an Instance's trail does not return them. They are read on the organization's trail, which holds every entry of the organization — the instances' included.

  1. Open Audit trail, at the bottom of the sidebar: the feed of the whole organization, newest first, grouped by day.
  2. Narrow it with the search box, the status filter, the time range — 24h, 7 days, 30 days or All time — and the Event type, Instance and Customer filters. An entry recorded without an Instance shows a dash in both columns.
  3. Click a row to expand it: Full payload is the event's data. Export downloads the filtered entries as a CSV file.
The Audit trail page of Kaiten Sushi Shop: a search box, a status filter and a time range above a feed of entries — flag evaluations, usage reports, a cap reached — and a grant, Entitlement assigned to a license, with a dash under Instance and Customer.The Audit trail page of Kaiten Sushi Shop: a search box, a status filter and a time range above a feed of entries — flag evaluations, usage reports, a cap reached — and a grant, Entitlement assigned to a license, with a dash under Instance and Customer.

The page reads the latest 500 entries, and Load older events widens the window to 1,000. To go further back, or to automate the read, use the API.

The dataset's own history. The demo profile seeds through the same use cases the API runs, so the trail of a seeded stack is the one Kaiten recorded while it seeded — every creation, grant, and deployment, dated at the moment the seeder ran — on a Community Edition stack as in the Cloud demo sandbox. It holds no flag evaluation until you make one.

Worked question 1 — "Why could a Sakura Tokyo user see delivery tracking?"

Support asks this. An evaluation entry does not answer it alone — it names the flag and the decision, not the customer. The contract does, in three hops:

  1. The License the instance ran under. On the trail of sakura-tokyo-prod, the INSTANCE_CREATED entry carries its licenseSlug — premium-v2, the second version of Premium — and no later INSTANCE_UPDATED entry changes it.
  2. What that License granted. On the organization's trail, the LICENSE_ENTITLEMENT_ASSIGNED entry for premium-v2: entitlementSlug delivery-tracking, value true, recorded when the License was configured.
  3. What the flag's rule said. On the organization's trail, the newest FEATURE_FLAG_CREATED or FEATURE_FLAG_UPDATED entry for delivery-tracking-enabled before the date in question: one rule, serving on where __kaiten.entitlements['delivery-tracking'] == true.

Three dated entries, and the answer is complete: from the moment all three held, an evaluation for Sakura Tokyo resolved to on, because its License grants the entitlement the rule reads. Where the flag was evaluated in the period, its FEATURE_FLAG_EVALUATED entries confirm that it served on with reason TARGETING_MATCH, and name the rule that matched.

The mirror question — why a Ninja Osaka user did not see it — is the same three hops from ninja-osaka-prod: a licenseSlug of starter, and a Starter grant of false. Gate a feature by License builds this chain and reads both evaluations back.

Worked question 2 — "Ninja Osaka says an order was blocked. Was it, and rightly?"

This is a dispute, from the customer or from finance. On your stack, ninja-osaka-prod holds such a refusal once you have run Meter usage and enforce limits, which takes its monthly orders to the cap and past it. Filter ninja-osaka-prod to ENTITLEMENT_USAGE_REPORT_REJECTED over the window in question — here, the last seven days:

AFTER=$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ)   # macOS: date -u -v-7d +%Y-%m-%dT%H:%M:%SZ

curl "$KAITEN_API/instances/ninja-osaka-prod/audit-trails?event_name=ENTITLEMENT_USAGE_REPORT_REJECTED&after=$AFTER" \
  -H "Authorization: Bearer $KAITEN_TOKEN"
{
  "items": [
    {
      "id": "…",
      "instanceSlug": "ninja-osaka-prod",
      "eventName": "ENTITLEMENT_USAGE_REPORT_REJECTED",
      "eventType": "com.kaiten.instance.entitlement.v1.usage_report_rejected",
      "timestamp": "…",
      "payload": {"entitlement_slug": "monthly-orders", "value": 1, "behavior": "append", "status": "REJECTED", "…": "…"}
    }
  ],
  "hasMore": false
}

Yes, it was blocked: one report of 1 against monthly-orders, refused. Was it rightly blocked? The INSTANCE_ENTITLEMENT_USAGE_REACHED entry just before it, on the same trail, carries the usage record at the moment the cap was hit — value 20, limit 20. The grant that applied is Starter's: a value of 20 with limitCapExceededOveragePercent 0, a hard cap with no allowance past it. Where a grant was made through the console or the API, its LICENSE_ENTITLEMENT_ASSIGNED entry is on the organization's trail — the demo profile's grants included, recorded at the moment the seeder ran — and GET /licenses/starter/entitlements/monthly-orders reads the grant itself. Refusal, cap, contract: the receipt is three records long, each one dated, and no entry of the trail can be edited.

The trail and the event stream

The trail is the read side: per instance or for the whole organization, filtered, kept — the place to answer a question after the fact. The event stream is the write side: the same events, delivered as they happen to a webhook endpoint on Kaiten Cloud Kaiten Cloud or to a consumer on your own broker when self-hosted. Use the stream to react; use the trail to prove. They read from the same record, so they never disagree.

Entries are append-only: the API exposes reads only, and there is no operation to edit or remove one. That is the property that makes the trail usable in a dispute or a SOC 2 review — the answer you give is the answer the system gave at the time, and nothing in between could have changed it.

Where to go next

On this page