Core Plugins

Status

Track the health of your services and display it with $status.


Setup

The status plugin is included in manifest.js with all core plugins, or can be selectively loaded. It activates when manifest.json contains a status block.

<!-- Meta -->
<link rel="manifest" href="/manifest.json">

<!-- Scripts -->
<script src="https://cdn.jsdelivr.net/npm/mnfst@latest/lib/manifest.min.js"></script>

Configuration

Add a status block to manifest.json. Each key becomes a top-level status at $status.<name>, rolled up from its underlying signals. A value can be a string (a single probe), an array (several signals rolled up), or an object (a single signal, or an aggregate with options).

manifest.json
{ "status": { "website": "https://example.com", "docs": ["https://docs.example.com", "https://cdn.example.com"], "api": { "url": "https://api.example.com/health", "degradedAbove": 800 }, "cloud": { "signals": [ { "appwriteService": "databases", "label": "database" }, { "mirror": "appwrite", "label": "upstream" } ], "rollup": "worst", "refresh": 30000 } } }

A single project can mix sources freely, with the provider behind each signal is inferred from which field is present.

Field Signal Behavior
url HTTP probe Fetches the endpoint; reads reachability and latency.
feed Status feed Reads a normalized status JSON the endpoint returns.
static Literal A fixed state — useful for placeholders or manual control.
appwriteService Appwrite Resolves against your Appwrite endpoint's /health.
mirror Upstream Reflects a known third party's own status.
heartbeat Dead-man's switch A job calls $status.beat(key); silence reads as down.
mcp Manifest hosting Hosted signal for projects published with Manifest AI.

Probes & Feeds

For a service you own, probe an endpoint directly or read a status feed it publishes.

  • url has the browser fetch the endpoint and infer state from the response (2xx is operational; a degradedAbove latency threshold marks it degraded). Simplest, but bound by CORS and run per visitor, so it suits same-origin or CORS-enabled endpoints.
  • feed reads a small JSON document your backend produces and maps its state onto the Manifest vocabulary. This avoids CORS and can represent what a probe cannot, such as background workers and queues.
{
    "status": {
        "api": {
            "url": "https://api.example.com/health",
            "expect": 200,
            "degradedAbove": 800
        }
    }
}

A network error, timeout, or CORS block resolves a probe to unknown rather than a false outage. The plugin never reports "down" when it simply could not reach the endpoint.


Mirrors

A mirror reflects a third party's own published status. Common providers are built in by name — github, cloudflare, stripe, openai, anthropic, discord, npm, vercel, netlify, appwrite — and any service running an Atlassian Statuspage works by passing its base URL directly.

Mirror examples
{ "status": { "payments": { "mirror": "stripe" }, "ai": { "mirror": "anthropic" }, "custom": { "mirror": "https://status.example.com" } } }

States

Every signal and entry resolves to one of these states.

State Level Meaning
operational 0 Healthy
maintenance 0.5 Planned downtime
degraded 1 Working but impaired (e.g. slow)
partial_outage 2 Some functionality unavailable
major_outage 3 Down
unknown -1 No signal yet, or could not be reached

A feed or mirror may return its own vocabulary (up, down, pass, minor, etc.) — the plugin maps common terms onto the table above.


Labels

$status.label(state) turns a state into a display string (major_outage → "Major Outage"). Labels default to English but localize through the _ui convention — the same mechanism as the datepicker (_ui.date) and colorpicker (_ui.colorpicker). Provide overrides under _ui.status.label in any loaded data source, per locale:

labels.fr.yaml
_ui: status: label: operational: Opérationnel degraded: Dégradé partial_outage: Panne Partielle major_outage: Panne Majeure maintenance: Maintenance unknown: Inconnu

$status.label(state) — and anything that uses it — then returns the localized string, reactive to the current locale.


Rollup & Options

Aggregate entries accept options alongside their signals.

Option Default Description
rollup "worst" How children combine: worst takes the most severe, best the least.
refresh 30000 Poll interval in milliseconds.
confirmations 1 Consecutive observations required before a state change (debounce).
staleAfter refresh × 3 Milliseconds without a known signal before stale becomes true.
history 90 Number of recent checks retained for the uptime meter.

By default, blocks with no options set or a bare "website": "https://example.com" will poll every 30 seconds, rolls up worst-first, and goes stale after 90 seconds.


Reading Status

$status.<name> returns a reactive health object. Read whatever you need; every read re-evaluates when the underlying signals update.

Property Type Description
state string Rolled-up state (see States).
up boolean true only when state is operational.
level number Severity, 0 (operational) to 3 (major outage), -1 unknown.
latencyMs number | null Average probe latency, when measured.
message string | null A human update attached to the state (from a feed or $status.set), or null.
stale boolean true when no fresh signal has arrived within the stale window.
updatedAt number Timestamp of the last resolution.
signals array The underlying inputs, each with its own label and state.
uptime number | null Percent operational over the retained history window.
history array Recent observed states, oldest to newest.
incidents array Open and recent incidents for this service.

The magic also exposes a few helpers:

Accessor Description
$status.overall Worst state across every entry.
$status.all The map of all entries.
$status.ready true once the first resolution pass has run.
$status.incidents All incidents across every service, newest first.
$status.label(state) Human label for a state string (major_outage → "Major Outage").

Address a service by name, or iterate $status directly — each item is a health object, so you read it with dot notation just like a $x source.


<p x-text="$status.API.state"></p>

<template x-for="(service, name) in $status" :key="name">
    <span x-text="name + ': ' + service.state"></span>
</template>

UI Templates

The plugin gives you state; the presentation is yours. These templates range from a full status page down to a single indicator — each reads the same $status values, including the built-in service.uptime, the service.history meter, and $status.incidents. History accrues live as the plugin polls (or is hydrated from a feed); incidents are logged whenever you post an update with $status.set.

<div class="col gap-10 w-full">

    <!-- Headline banner -->
    <div class="row items-center gap-3 p-4 bg-page border border-line rounded-lg">
        <figure style="size-6 rounded-full"
            :class="{
                'bg-positive-content':$status.overall==='operational',
                'bg-yellow-400':$status.overall==='degraded',
                'bg-orange-400':$status.overall==='partial_outage',
                'bg-negative-content':$status.overall==='major_outage',
                'bg-surface-3':$status.overall==='maintenance',
                'bg-surface-3':$status.overall==='unknown'
            }"></figure>
        <p x-text="$status.overall==='operational' ? 'All systems operational' : 'Some systems are experiencing issues'"></p>
    </div>

    <!-- Status list -->
    <template x-for="(service, name) in $status" :key="name">
        <div class="col gap-1">

            <!-- Title -->
            <div class="row items-center justify-between">
                <strong x-text="name"></strong>
                <small x-text="service.uptime + '% uptime'"></small>
            </div>

            <!-- Daily status -->
            <div class="row" style="gap:2px;">
                <template x-for="(day,i) in service.history" :key="i">
                    <figure x-tooltip="`${$status.label(day)}`" class="flex-1 h-6 rounded-xs"
                        :class="{
                            'bg-positive-content':day==='operational',
                            'bg-yellow-400':day==='degraded',
                            'bg-orange-400':day==='partial_outage',
                            'bg-negative-content':day==='major_outage',
                            'bg-surface-3':day==='maintenance',
                            'bg-surface-3':day==='unknown'
                        }"></figure>
                </template>
            </div>

        </div>
    </template>

    <!-- Incident history -->
    <div class="col gap-4">
        <strong>Past Incidents</strong>
        <template x-for="inc in $status.incidents" :key="inc.id">
            <div class="row items-center gap-3 p-2 border-t border-line">
                <small class="min-w-14 capitalize" x-text="inc.name"></small>
                <div class="capitalize"><span x-text="inc.message || $status.label(inc.state)"></span><small x-text="inc.resolved ? 'resolved' : $status.label(inc.state)"></small></div>
            </div>
        </template>
        <small x-show="!$status.incidents.length" class="text-content-subtle">No incidents reported</small>
    </div>
</div>

Manual Control

The magic exposes methods for operators and background systems.

Method Description
$status.set(name, state, message?) Force a state and an optional update message, overriding signals.
$status.clear(name) Remove an override and return to signal-derived state.
$status.refresh(name?) Re-resolve one entry, or all when called with no argument.
$status.beat(key) Pulse a heartbeat signal; absence past its window reads as down.

<button @click="$status.set('API', 'major_outage', 'Investigating elevated error rates')">Report outage</button>
<button @click="$status.clear('API')">Resolve</button>

<small class="capitalize" x-text="$status.API.state"></small>
<p x-show="$status.API.message" x-text="$status.API.message"></p>

Update Messages

A message is a human note attached to a status — the line you post during an incident, like "Investigating elevated error rates". It rides alongside the state and surfaces as $status.<name>.message, or null when there is none. It can be set one of two ways:

  • Manually, as the third argument to $status.set — for operator actions and incident updates:
<button @click="$status.set('api', 'degraded', 'Investigating elevated error rates')">Post update</button>
  • From a feed, by returning a message next to the state. The plugin reads both:
status.json
{ "state": "degraded", "message": "Elevated latency in the EU region" }

Clearing the override (or the feed dropping its message) returns message to null.