Health and tier
Liveness, plan tier, licence refresh state and the narrow config read/write the dashboard uses.
| Method | Path | Auth |
|---|---|---|
| GET | /api/health | Open |
| GET | /api/tier | Open |
| GET | /api/license/status | Open |
| GET | /api/onboarding/status | Open |
| GET | /api/config | Dashboard token |
| PUT | /api/config | Dashboard token, x-grith-csrf |
GET /api/health
status is healthy when every subsystem reports ok, degraded if any is degraded, and
unhealthy if any errored. The four subsystems are audit, digest, proxy and
supervisor.
The body carries status, version, uptime_seconds, a subsystems map of
{status, message?, latency_ms?}, instance_id and protocol_version.
Two optional fields matter more than the rest. audit_quarantined and audit_read_only
appear only when set, and they are on the unauthenticated payload deliberately: the CLI
reads them before starting a supervised session, so a daemon that cannot durably record
decisions is caught before a tool is spawned. instance_id is regenerated on every start,
which lets a client tell "the daemon that admitted me" from "the daemon that replaced it" -
something a PID cannot express.
GET /api/tier
tier, seats, max_sessions, renewal_date, billing_portal_url, refresh,
session_limit_rejections and session_limit_rejection_window_days, plus features.
max_sessions is the licence ceiling - 2 on Community, 64 on a paid plan - not the value
of supervisor.max_concurrent_sessions. The lower of the two wins at session registration.
features is a flat map of 13 booleans. It is a JSON object, so read it by key - the
serialised order is not stable:
{
"proxy": true, "audit": true, "digest": true, "supervisor": true,
"filters": true, "cli": true, "dashboard": true,
"notification_channels": false, "usage_analytics": false,
"cloud_sync": false, "policy_editor": false,
"pagerduty": false, "opsgenie": false
}The first seven are true on every tier. The next four are what a Pro licence turns on.
pagerduty and opsgenie are always false.
GET /api/license/status
The refresh-health view, kept separate from /api/tier so a caller that only needs
"is my licence still fresh" does not pull the feature list: tier, seats,
renewal_date, billing_portal_url, air_gapped, hours_since_refresh and the same
refresh block. This is what grith pro status reads.
GET /api/config
Three fields: config_scope (the two scope names, local and team - not paths),
proxy (the effective auto_allow_threshold and auto_deny_threshold), and filters -
one entry per filter with id, name, phase and enabled. Both the thresholds and each
enabled are the effective values: the running defaults, then team-config.toml, then
config.toml on top.
PUT /api/config
Not a general config writer. It accepts four things and nothing else: a scope of local
or team, a filters array of {id, enabled}, proxy.auto_allow_threshold and
proxy.auto_deny_threshold, and server.auto_open_dashboard.
{
"scope": "local",
"proxy": { "auto_allow_threshold": 3.5 },
"filters": [ { "id": "behavioural", "enabled": false } ]
}An unknown filter id is 400 UNKNOWN_FILTERS. Thresholds outside 0.0-10.0, or an allow
threshold at or above the deny threshold, are 400 INVALID_PROXY_THRESHOLD. Success is
{status, scope, filters_updated, proxy_updated, server_updated, message}. The write lands
in config.toml, or team-config.toml when scope is team; the running proxy keeps its
current thresholds until the daemon restarts.