Skip to content

Settings

The settings page (/orgs/:customerId/settings) uses a tabbed layout:

  1. General — displays the organization name (editable by owners via inline click-to-edit) and the organization ID
  2. Members — team management with invite, role assignment, and member removal
  3. Roles — custom role management with granular permission matrix
  4. Teams — named groups of members whose granted roles are inherited by every member (visible with teams:read; managed with teams:admin)
  5. API keys — API key creation and revocation for dashboard/programmatic access
  6. Orchestrator keys — orchestrator API key management for Platform WebSocket connections
  7. Sources — read-only list of registered webhook sources (see below)
  8. Billing — plan and payment management
  9. CI trust — trust policy configuration for CI runs (visible with ci_trust:read permission)
  10. Global workflows — org-level security knobs for cross-repo workflows (visible with org_settings:read permission)
  11. Webhooks — outbound webhook endpoint management with delivery logs and test ping
  12. Event log — inbound webhook delivery log (visible with event_log:read permission)
  13. Security — dashboard-level security controls for this org: the session max age (every member sees it; only an organization owner can change it) and the orchestrator token reconciliation report (visible with members:admin)
  14. Orchestrator security — read-only view of the orchestrator’s dashboard-write policy matrix (visible with org_settings:read permission)
  15. Support access — opt-in switch that controls whether KiCI support staff may open read-only support sessions against your org (visible with support:read; toggled with support:admin)

Audit-log-style entries are not a settings tab; they live on the dedicated Activity page accessible from the sidebar.

Tab selection syncs with the URL path (/settings/members, /settings/api-keys, etc.), making tabs bookmarkable.

General settings show your organization’s basic information, including the org name (editable by owners) and the unique organization ID. Use this to rename your org or reference the ID for API calls and configuration.

The members tab lets you manage your team:

  • Invite new members by email.
  • Assign roles.
  • Suspend or remove members.

Each member’s effective CI trust level is shown, alongside their linked provider accounts (e.g. GitHub). Change the level by changing the roles the member holds.

What suspension revokes. A suspended member holds no CI approval authority. Your orchestrator receives their CI trust as none. They also satisfy no {team} approver clause, because they are dropped from every team roster your orchestrator receives. Their linked provider accounts stay linked, so commits and pull requests they already opened still resolve to them instead of falling under your fork policy as unknown contributors. The badge in this tab keeps showing the level their roles grant, because that is the level unsuspending restores, together with their team memberships. Both the suspend and the unsuspend reach your orchestrator immediately.

Roles define granular permissions across resource categories (runs, secrets, members, etc.) with 5 access levels: none, read, read_payload, write, admin.

Create custom roles to restrict what team members can do, or use the built-in Owner role for full access. The permission grid offers a per-column “set all” and a “reset all to none” for quick edits.

When you create or edit a role, you can only grant permissions you already hold — levels above your own role show a 🔒 in the grid and are rejected server-side, so a role can never hand out access beyond its author’s.

Teams are named groups of organization members. A role granted to a team is inherited by every member, so you can manage permissions for a whole group in one place.

Team names can also be referenced in workflow approval gates (approval: [{ team: 'leads' }]) — any member of the named team can satisfy that gate.

Managing teams (create / rename / delete, membership, role grants) requires the Teams permission at admin; read shows a view-only list.

API keys allow programmatic access to the KiCI API for automation, scripts, and CI integrations.

Each key is scoped to this organization with a custom permission matrix and an optional expiry date. Keys can be revoked individually.

The permission matrix starts at none for every resource — you grant only what the key needs. Use a level column’s Set all button to apply that level everywhere at once, or Reset all to none to clear it. You can only grant up to your own role: levels above your ceiling are locked, and the server rejects any key whose permissions exceed yours.

Set a key’s kind to Agent in the creation modal (and give it an agent name) to mint a provenance-carrying service account: an agent org key drives the coding-agent MCP server and stamps its agent label on every audited action. Agent keys are marked with an Agent badge in the list so you can tell them apart from ordinary programmatic keys at a glance.

Use a key’s clone button to open the creation modal prefilled with that key’s name, expiry, and permissions — handy for recreating an expired key or deriving a new key from an existing one.

Orchestrator keys authenticate the WebSocket connection between your orchestrator and the KiCI Platform relay.

Create a key here and set it as the KICI_PLATFORM_TOKEN environment variable in your orchestrator configuration. Keys can optionally be restricted to specific routing patterns.

Use a key’s clone button to open the creation modal prefilled with that key’s name and description.

The orchestrator keys tab manages API keys used to authenticate orchestrator-to-Platform WebSocket connections. These are separate from user API keys (which grant dashboard/API access).

List view — shows all active orchestrator keys with name, description, key prefix, creation date, and last used date.

Create — opens a modal to enter a name and optional description. After creation, the raw key is shown once in a copyable box. Set this key as the KICI_PLATFORM_TOKEN environment variable in your orchestrator configuration.

Revoke — opens a confirmation modal before soft-deleting the key. Any orchestrators using the revoked key will be disconnected.

Webhook sources are registered automatically when an orchestrator connects to the Platform.

Each source shows its routing key and full webhook URL — configure this URL in your provider’s webhook settings (e.g. GitHub App).

GitHub App sources also show a kici-admin source get-webhook-secret <routingKey> command — run it on the orchestrator to retrieve the secret used to verify webhook signatures.

The sources tab shows webhook sources registered by connected orchestrators. Sources appear here automatically when an orchestrator connects to the Platform — there is no manual “add source” action in the UI.

Each source displays:

  • Routing key — the source identifier (e.g., github:12345 for a GitHub App, generic:my-source for a generic webhook)
  • Webhook URL — the URL to configure in your provider’s webhook settings
  • Registered at — when the orchestrator first registered this source
  • Copy button — copies the webhook URL to the clipboard

Read-only — sources cannot be created, edited, or deleted from the dashboard. They are managed entirely by orchestrator connections. When an orchestrator disconnects, its sources remain visible (they are not automatically removed).

Empty state — if no orchestrator has connected yet, the tab shows “No webhook sources registered” with a link to the operator setup guide.

Webhook secrets — webhook secrets are not visible in the dashboard. They are configured on the orchestrator (see the operator guide), never in the dashboard, and are used to verify incoming webhook signatures.

Adding a new source requires:

  1. Configure a new provider in the orchestrator (e.g., add a GitHub App to the orchestrator’s provider config)
  2. Configure the webhook secret on the orchestrator (see the operator guide)
  3. Restart the orchestrator — it will register the new source with the Platform on connection
  4. Configure the webhook URL (shown in the sources tab) in the provider’s settings (e.g., GitHub App webhook URL)

The billing tab shows your current plan (Free, Pro, Team, or Business), resource usage meters, and lets you upgrade to a paid tier.

Choose Monthly or Annual billing, click a tier’s “Upgrade” button to start a Stripe Checkout, or use “Manage payment” to switch tiers and update payment methods via the Stripe Billing Portal.

The tab also has a Billing email field — the address KiCI sends subscription messages to (a confirmation when your plan changes, and a notice if your plan is downgraded). A source badge shows whether it was set here (Manual) or synced from the payment provider (From Stripe). Set a billing email before upgrading from Free — the Upgrade buttons stay disabled until one is set. A billing admin can edit it any time.

The usage meters track:

  • Members: invited users in this org.
  • Orchestrators: every orchestrator instance counts — coordinators and workers alike. A coordinator opens a Platform connection and reports its connected workers. A worker does not open its own connection, but still counts toward the limit.
  • Relayed webhooks (this month): webhooks delivered through the Platform relay during the current billing window.
  • Runs (this month): runs started during the current billing window. Going past this cap does not stop or change anything: your orchestrator runs your workflows, not the Platform, and a run past the cap is recorded, shown, and notified about exactly like any other. The meter is there so sustained growth is visible before you are talking to us about it.
  • Live log minutes (today): live-log streaming time consumed in the current UTC day. On the Free tier this is capped at 60 minutes/day; paid tiers have no daily cap (shown as unlimited).
  • Live-log tail-minutes (24h): live-log streaming time consumed in the trailing 24 hours, against the paid-tier rolling fair-use cap (Pro 5,000 / Team 20,000 / Business 50,000 tail-minutes). The Free tier has no separate tail cap (shown as unlimited) because it is bounded by the daily limit above. Only live tailing of a running job counts — stored and completed-run logs are never metered.
  • Retention period: how long execution history is kept.

The infrastructure page and this tab count the same orchestrators — coordinators plus workers.

The billing email is where KiCI sends subscription messages: a confirmation when your plan changes, and a notice if your plan is downgraded.

Set it before upgrading from Free to a paid plan. Once you subscribe, KiCI keeps it in sync with the email on file with the payment provider. A billing admin can edit it here at any time.

The orchestrators counter measures how many orchestrator instances your organization runs — coordinators and workers alike. Each instance counts as one.

What counts as one orchestrator:

  • One standalone orchestrator (single process, no cluster) → 1.
  • One cluster of 1 coordinator + N workers → 1 + N. The coordinator opens a Platform connection and reports its connected workers; a worker does not open its own connection, but still counts.
  • N independent orchestrator deployments (e.g., one per environment, one per region) → N, plus each deployment’s own workers.

The infrastructure page and the billing meter count the same instances, so the two numbers agree.

When you hit the cap, the next coordinator that tries to connect is rejected, and a worker that tries to join a coordinator past the cap is refused — the orchestrator logs a plan-limit error. If your organization stays over the cap for 7 days, its newest workers are drained and disconnected down to the limit. Upgrade your plan to lift the cap; the meter updates immediately.

The relayed-webhooks counter only includes webhooks delivered through the KiCI Platform relay — the route at kici.dev that verifies an inbound webhook and forwards it to your orchestrator.

Webhooks pointed directly at your orchestrator’s public ingest endpoint never reach the Platform, so they’re invisible to this counter and uncapped on every Hosted tier. If you have a public orchestrator ingress, you can mix-and-match: use the relay for sources you can’t expose publicly, and point GitHub (or any provider / generic webhook) straight at your orchestrator for the rest.

Every webhook the relay forwards counts — including ones your workflows ultimately ignore. Trigger matching runs on your orchestrator, not on the Platform, so the relay forwards each verified webhook before any trigger is evaluated. A source that sends many events you filter down to a handful of runs still consumes one relayed webhook per event. If a high-volume source mostly produces no run, point it directly at your orchestrator (see above) to keep it off this counter entirely.

When you hit the cap, new relayed webhooks are rejected with 429 Plan limit reached. Upgrade in the Stripe Billing Portal to lift the cap immediately; usage resets monthly on your billing anniversary.

Switch the prices shown on the tier cards between US dollars and euros. The choice you pick here is also the currency Stripe charges in when you click “Upgrade”.

The default is detected from your browser language. EU, EFTA, and UK locales default to euros; everywhere else defaults to dollars.

Your choice persists in a 90-day cookie (kici_pricing_currency), so it survives across reloads and applies on every billing page.

This banner appears when your organization’s latest payment to Stripe has failed. Your subscription remains active during the retry period, but you should update your payment method promptly to avoid service interruption.

Three things live on this tab.

  • The fork switch: what happens to a pull request from a fork. ignore drops it, hold parks it for approval, allow runs it. A fork run always runs with reduced privilege — base-branch workflow definitions, no install or registry secrets, and an isolated build cache. While the switch is on ignore, the tab shows a warning: every fork pull request is discarded before it runs, with no run, no check status and nothing visible to the contributor, so a maintainer cannot tell the setting from a broken CI system. Each drop is recorded on its delivery in the event log, and an admin can switch to hold from the warning itself.
  • Approval expiry: how long a held run stays approvable before it expires, entered as a number plus a unit (seconds, minutes, or hours), between 1 second and 1 year.
  • A link to the approval queue: the page that lists every hold, security and context alike.

CI trust is approval authority, not code trust. A member with ci_trust:write can release a hold; ci_trust:admin can change the switch. Grant the level through a role the member holds, directly or through a team.

Trust itself comes from the git ref, so nothing here changes how much a run is trusted.

Global workflows let a single “workflow repo” define jobs that run when events happen in other repos in the same org.

This tab exposes the security knobs as independent axes:

  • Master switch (read-only): the fleet-wide on/off state, held by the orchestrator operator. Shown here as a badge; set it with kici-admin cluster-settings.
  • Authoring allow-list: which repos may define global workflows.
  • Source deny-list: source repos whose events never trigger globals (forks, public-contrib).

See the user guide and the architecture reference for the full model.

Fleet-wide master kill-switch for global workflows, held by the orchestrator operator. This badge is read-only — it reports the effective cluster state (cluster_settings.global_workflows_enabled) and cannot be flipped from the dashboard. The operator sets it with kici-admin cluster-settings set --global-workflows-enabled true.

  • Disabled cluster-wide: the orchestrator will not register any workflow that declares repos: patterns, and will not dispatch cross-repo triggers — effectively rolling every org back to per-repo-only semantics. All other settings on this page are ignored.
  • Enabled cluster-wide: the per-org lists below become your safety rails.

Restricts which repos in this org may define global workflows (the “authoring axis”).

  • OFF: any repo in the org may declare a workflow with repos: patterns and have it registered.
  • ON: only repos whose identifier matches one of the entries below may author globals. Non-matching repos have their global workflows dropped at registration time, with a warning in the orchestrator log.
  • ON + empty list: no repo may author globals — use as a temporary lock-down.

Each entry has two parts:

  • Source: pick a configured source (a specific GitHub App or universal-git source) to pin the entry to that source only, or leave it as Any source to match across every source in the org.
  • Pattern: a glob matched against the authoring repo identifier (e.g. myorg/ci-*, myorg/platform-*).

Pinning by source is useful when the same owner/repo could legitimately exist on more than one configured source and you only want to trust one of them as an author.

Deny-list for source repos whose events must never trigger a global workflow (the “source axis”).

Use this for untrusted territory — forks, public-contrib mirrors, sandboxes — where a single push shouldn’t be able to fan out org-wide automation.

Evaluated at dispatch time against the repo that emitted the event, independently of the authoring allow-list: a global workflow whose author is allowed will still be skipped if the source repo is denied. Both lists can be active simultaneously.

Each entry has two parts:

  • Source: pick a configured source to deny only events delivered on that source, or leave it as Any source to deny across the org.
  • Pattern: a glob matched against the source repo identifier (e.g. myorg/fork-*, myorg/public-*).

Pinning by source is the right move when the same owner/repo is reachable through more than one configured source (e.g. a public forge and a trusted mirror) and you want to drop deliveries from only one of them.

Configure outbound webhook endpoints to receive notifications when runs and jobs change status. Each endpoint receives HMAC-SHA256 signed payloads with event details.

For each endpoint you can:

  • Subscribe to event types: run.started, run.completed, run.failed, job.started, job.completed, job.failed.
  • View delivery logs: HTTP response codes and retry counts.
  • Send a test ping: verify connectivity before going live. A ping the endpoint accepts confirms with its HTTP status; a ping the endpoint rejects reports a warning and is recorded in the delivery log with the failure reason, so a broken receiver is never reported as a success.

Endpoint URLs must meet three requirements:

  • HTTPS only. An endpoint URL must use https:. Any other scheme is rejected when you save the endpoint.
  • Publicly routable address only. A URL that points at a private (RFC1918), loopback, link-local, unique-local, carrier-grade-NAT, multicast, or otherwise reserved address is rejected. Obvious cases are caught when you save; every delivery additionally resolves the hostname and connects only to the validated public address, so a name that resolves to a private address fails at delivery time with a generic “blocked” error. To receive notifications on a private network, put a publicly reachable relay in front of your receiver.
  • No redirects. A delivery is never followed to a Location: target — a receiver that answers with a 3xx records that status as a failed delivery, with an error explaining that the endpoint redirected and that webhook targets must respond directly. Publish the final URL directly. Redirect failures count toward the consecutive-failure budget like any other failure, so an endpoint that keeps redirecting is eventually disabled automatically.

The delivery log shows recent webhook deliveries for an endpoint, including the HTTP status code, number of retry attempts, and the event payload.

Retry behavior:

  • Deliveries are retried up to 3 times with exponential backoff.
  • After 10 consecutive failures, the endpoint is automatically disabled — you can re-enable it from this view.
  • At-least-once delivery: a delivery survives a Platform restart, so recovering an interrupted attempt can occasionally re-send one your receiver already processed.
  • Deduplicate on X-KiCI-Delivery: every attempt for the same delivery repeats that header value — treat it as the idempotency key in your receiver.

Organization-wide notification channels, Slack connections, subscriptions, and the delivery log live on the dedicated Notifications page (in the org sidebar), on its Organization tab. The /settings/notifications URL redirects there.

The event log shows every inbound webhook this organization has received, regardless of whether it came in via the Platform relay or directly to an orchestrator.

Each row shows the inbound webhook’s metadata and, with the right permission, its raw payload and processing outcome.

Filter by routing key, event type, status, or delivery ID. Click a row for the full detail plus the payload viewer.

The detail panel shows the inbound webhook’s full record:

  • Delivery: where the webhook arrived and how it was routed.
  • Outcome: what happened next — the matched workflow count and links to any runs it spawned.
  • Workflow decisions: every workflow the delivery was evaluated against, and the checks the trigger evaluation performed for each.
  • Payload: the raw webhook body, loaded on demand. Requires event_log:read_payload.

Oversized payloads show an “omitted” badge.

The event log tab (/orgs/:customerId/settings/event-log) shows every inbound webhook this organization has received. The event log records, for each delivery: routing key, event, action, repo, status, processing outcome (shed / processed / duplicate / lockfile_missing / failed), the matched workflow count, and the first run spawned (if any).

The list view supports filters for routing key, event type, status, and free-text delivery ID search. Click a row to open a detail panel with the full delivery record plus the payload viewer.

Permissions:

  • event_log:read — list rows and view metadata in the detail panel.
  • event_log:read_payload — additionally view the raw webhook payload body, and the parts of the decision trace that quote it. (Owners and admins inherit this. Lower-tier roles see “Payload not available” with a hint to ask for an elevated role.)

Workflow decisions:

The detail panel shows the per-workflow decision trace the orchestrator recorded for the delivery. Each entry names a workflow, says whether it matched, and expands to the individual checks the trigger evaluation performed: the check, the pattern, the value tested against it, whether it passed, and the reason.

An organization-wide workflow lives in a different repository from the one the event came from, so its entry also names the repository that defines it. Use this to see why an organization-wide workflow did or did not fire — see Organization-wide workflows.

Parts of the trace quote the webhook body: the value a check tested, and the reason it gives. Those two fields need event_log:read_payload, the same permission as the payload viewer. Without it the row still names the check, the pattern, and whether it passed, and the value and reason read [redacted — requires event_log:read_payload].

A trace is bounded in two places, and each bound says what it did. A workflow with very many checks keeps the first of them, and the entry says how many checks it kept out of the total. A delivery with very many workflows keeps the decisions that fit, and a note under the list says how many further decisions were dropped. A delivery the Platform rejected before trigger matching has no trace at all.

Edge cases the UI surfaces:

  • Payload omitted — when the inbound payload exceeded the configured size cap, the row is still recorded with the payload shown as “omitted”.
  • Orchestrator unavailable — when the orchestrator does not respond in time, the list still loads with the delivery metadata only, marked with an orchestrator unavailable banner.
  • Orchestrator-only deliveries — direct-ingress deliveries that never crossed the Platform appear marked orchestrator_only.

Retention is 30 days.

Dashboard-level security controls for this organization.

Session max age controls how long a dashboard sign-in stays valid before the browser must re-authenticate. It applies to dashboard sessions only — CLI sessions are unaffected.

  • Range: 1 hour to 30 days. The default is 7 days.
  • Owner-gated: every member sees the current value. Only an organization owner can change it — the control is read-only for everyone else. The API enforces org_settings:write on the change.
  • Enforced per organization: the limit is checked against the org whose data you are accessing, so a member of several organizations only re-authenticates when they touch data in an organization with a stricter limit. Re-authenticating restores access everywhere.

When a session exceeds the configured max age, the dashboard prompts for a fresh, full sign-in.

Your orchestrator authorizes kici-admin tokens on its own, so it keeps working when the Platform is unreachable — and nothing keeps its tokens in step with dashboard roles. This section reads the orchestrator’s live admin tokens, joins each one against your organization’s membership and roles, and reports where the two disagree. It is shown only to members holding members:admin.

A summary strip counts the tokens per category, and a table lists every token that drifts (token label and routing-key scope, orchestrator role, recorded holder, finding):

  • Unlinked — the token records no holder. Re-issue it with kici-admin token create --subject, then revoke the old one.
  • Orphaned — the recorded holder is not an active member of this organization. Revoke the token.
  • Over-privileged — the token grants more on the orchestrator than its holder has in the dashboard; the row names the missing permissions. Widen the dashboard role, or issue a narrower token.

A token’s holder is advisory metadata recorded at creation; the orchestrator never verifies it. When the orchestrator does not answer, the section shows an error rather than an empty report — no answer is not “no drift”. See RBAC: two layers for the full model.

Read-only view of the orchestrator’s dashboard-write policy.

Each row toggles one mutating dashboard action — setting a secret, approving a held run, retrying a dead-lettered webhook, and so on. The orchestrator operator decides which actions stay on the dashboard and which become CLI-only. The dashboard cannot change the policy itself — that’s the point: disabled actions stay off the dashboard entirely.

Manage the policy with:

  • Show the full policy: kici-admin org-settings dashboard-writes show
  • Disable an operation: kici-admin org-settings dashboard-writes set --op <name>=false
  • Reset to permissive defaults: kici-admin org-settings dashboard-writes reset

The summary strip at the top shows total / enabled / disabled counts plus whether your orchestrator is currently connected. A disconnected orchestrator means the page falls back to the cached policy from the most recent connection.

Controls whether KiCI support staff may open read-only support sessions against your organization. Sessions are off by default — nobody outside your org can read your data until you opt in here.

When enabled:

  • KiCI staff can open time-boxed, read-only sessions to investigate an issue.
  • Every read they perform is recorded in your audit trail with the support reason.
  • No writes are ever possible during a session.

Disabling the toggle immediately ends any in-progress support session. Only users with the support:admin permission (owners by default) can change this setting.

The Support access tab controls whether KiCI support staff may open a read-only support session against your organization to help diagnose an issue. The setting is off by default — until you opt in here, no one outside your org can read your data.

When support access is enabled:

  • A KiCI operator can open a time-boxed (30-minute, renewable), read-only support session scoped to a stated reason.
  • A support session is runs-only: the operator can browse your run list and, by confirming each run individually, view that run’s detail and step logs. Nothing else is visible, and no write is ever possible.
  • Every run an operator opens is recorded in your Activity audit trail, attributed to the operator with the support reason — so you can see exactly what was looked at and why.

Disabling immediately ends any active session. Toggling the switch off closes every in-progress support session for your org at once. Enabling and disabling the setting is itself audited, attributed to the user who changed it.

Viewing the setting requires the support:read permission; changing it requires support:admin (granted to owners by default).