Skip to main content
Yanked

This release has been yanked by its maintainers, and will be ignored by installers, except when explicitly specified.
Consider using release 0.5.3 instead.

Continuity Guard

Your Claude, ChatGPT or Grok subscription meters you against a rolling window. Exhaust the weekly one and you wait days; you cannot top a plan up, and no provider will tell you in advance how close you are.

Continuity Guard reads that number off your own machine, warns long-running jobs before the window closes, and writes down where the work got to. It can also watch agent sessions for the failures that never raise an error, and terminate them, but only if you turn that on.

Zero dependencies. Python 3.11+. Linux, macOS, Windows, or a container.

pipx install continuity-guard      # or: pipx install git+https://github.com/ak2tx/continuity-guard
cg quota                           # read-only, nothing to configure
PROVIDER   WINDOW         USED  RESETS               AGE  STATUS
openai     primary       97.0%  Sat 11:19            2m   ok
grok       weekly        63.0%  Sat 23:21           23m   ok
anthropic  five_hour     94.0%  Sat 12:30            0m   ok     <- after one wrapped Claude job
           seven_day     73.0%  Mon 02:00            0m   ok

Those numbers come from files the official clients already write, and from stream events passing through cg wrap. Each adapter was checked against the provider's own usage screen; the working is in docs/quota-accuracy.md.

What you will actually see

This varies by provider more than you would expect, so it is the first thing to say rather than a footnote.

you cg quota shows
use the codex or grok CLI their windows immediately. Those clients write usage to disk themselves.
use the claude CLI nothing, until you wrap one session
use claude.ai or ChatGPT in a browser nothing, ever

Claude is the awkward case. It does publish a percentage, but only as a rate_limit_event inside its stream-json output, and it never writes that to disk. Polling a file will not find it. The number has to be caught in flight:

cg wrap -- claude -p "whatever you were going to run anyway"
cg quota                           # the anthropic rows are there now

cg wrap runs your command unchanged and terminates nothing. It says so on its first line, every time.

Browser sessions leave no local record at all. If that is how you use your subscription, this tool has nothing to read and you should stop here.

Warn the job before the window closes

Reading the number is useful on its own. The reason it exists is what comes next: telling a running job that it is nearly out of room, while there is still room to do something about it.

from continuity_guard import guard

with guard("nightly-refactor", profile="overnight") as s:
    for step in agent.run():
        s.progress(step=step.name, tool=step.tool, args=step.args,
                   tokens=step.tokens, depth=step.depth)
        if s.should_wrap_up():          # cheap, non-blocking, False by default
            save_state(s.headroom())    # what to save is yours to decide
            break

Every signalled session also gets a handoff written for it, whether or not it answers. That matters because the sessions most worth preserving are usually the ones too wedged to preserve themselves. Be clear-eyed about what that document is: see what the handoff is and is not.

The watchdog, when you want it

Twelve detectors cover the agent failures that return HTTP 200 while nothing useful happens: loops, oscillation, pace collapse, token burn, runaway recursion, crash loops.

It ships observe-only. Nothing is terminated until you have watched it against your own traffic and decided to arm it.

cg init && cg install              # scaffold config, run as a service
cg status                          # how close each session is to firing, now

The order of this page is the order we suggest you adopt it. Reading the quota costs you nothing and needs no permission. The wrap-up signal costs one line in your loop. Kill authority comes last, because it is the only part that can take something away from you.


Why a heartbeat is not enough

A heartbeat proves a thread is running. It says nothing about whether work is happening, and the expensive failures are the ones where every request returns 200, latency sits inside the SLO, and the bill triples overnight.

So there are twelve detectors:

Detector Catches Signal
death process gone socket EOF, free from the kernel, no timeout
silence wedged, blocked forever no frames at all
no_progress the lying heartbeat: alive, not working frames arrive, seq frozen
decay pace collapse consecutive slow gaps vs the session's own learned baseline
repeat the agent loop identical tool + identical args, repeating
cycle oscillation / thrash edit, test, revert, repeating
burn the failure that never errors tokens/min high and no progress
budget all-night spend cumulative token ceiling, progress or not
deadline runs that never end wall-clock cap on one session
errors retry storms failed tool calls over a window
depth runaway sub-agent spawning recursion depth exceeded
flap crash loops repeated death + restart of the same name

Four of those are invisible to ordinary monitoring: repeat, cycle, burn and errors.

Three of them are easy to confuse with each other, so:

cycle exists because repeat cannot catch thrash. The classic agent failure is edit, run tests, revert, edit, run tests, revert. Every call differs from the one before it, so exact-duplicate detection sees healthy variety while the agent goes nowhere.

budget is not burn. burn is a rate, and an agent can stay under every rate limit and still spend all night. An absolute ceiling is the "one stuck agent spends $6,000 tonight" guard, and it fires whether or not work is progressing. Enforcement happens between steps, so a real total can overshoot by roughly one model call. Treat it as a ceiling, not a guarantee.

decay is the only adaptive detector. It learns a session's own pace from its first steps, freezes that baseline, then fires if the pace collapses relative to it. A 2s-per-step interactive agent and a 90s-per-step build agent both work with no tuning.

Four constraints keep decay safe. Each was added after a measurement showed the previous version firing on ordinary build traffic:

  • The effective threshold can never fall below the floor you set. Adaptation only ever loosens. A detector that can tighten itself will invent false positives on a workload it mis-learned.
  • The warn level is relative and sensitive (base * warn_k, floor 45s). The hard level is an absolute backstop (hard_floor, default 200s) set above the slowest gap a healthy heavy-tailed workload produces, so build and research sessions cannot physically reach the terminate level.
  • The baseline is a central quantile of the early gaps (baseline_q, default the median). Decay asks whether the typical pace collapsed. The upper tail belongs to silence, which is asking a different question.
  • It requires consecutive slow gaps. "3 of the last 7 steps were slow" just describes a build-heavy workload. An unbroken run is what a real collapse looks like.

Those defaults come from measured sweeps on build-heavy traffic, where 22% of steps take 30 to 150 seconds. Moving min_gaps from 3 to 4 to 5 took warn-level false positives from 12.5% to 1.2% to 0.4%, with detection at 100% throughout, so min_gaps = 5 ships. Adding hard_floor took the hard-level false positive from 1.2% to 0.0% at high sample counts with recall unchanged.

Death detection costs nothing at all. The session holds one socket open; when the process dies the kernel closes it and the daemon knows in milliseconds. That is an OS fact rather than an inference, so there is no polling, no timeout, and no way for it to be wrong.

RETIRE is not optional. Without a clean-exit signal every successful run ends in a socket close indistinguishable from a crash, and the tool alarms on its own users' happy path. The context manager sends it on normal exit and on exception, because an exception is still an observed, intentional end.

Tuning

Everything is adjustable per session, and thresholds reload live.

session inline overrides   >   named profile   >   defaults
guard("agent")                                   # defaults
guard("agent", profile="tight")                  # built-in profile
guard("agent", profile="loose",
      silence={"warn": "10m", "hard": "2h"})     # inline override

Four profiles ship. tight is for interactive work where you want fast feedback. loose is the false-positive-safe end, for long tool calls and research. overnight is for unattended runs: it catches loops and burn early and will kill a hard stall. wallet sets a low per-session token ceiling, for when a runaway bill would hurt more than a truncated run. Define your own in the config.

Nobody should have to read the source to find out what they are arming, so here are the numbers that differ from the defaults:

silence warn/hard no_progress warn/hard budget hard action
defaults 3m / 30m 3m / 45m 1,000,000 notify
tight 20s / 2m 60s / 5m 1,000,000 notify
loose 5m / 45m 15m / 2h 4,000,000 notify
overnight 2m / 15m 10m / 45m 1,500,000 terminate
wallet 3m / 30m 3m / 45m 25,000 notify

overnight is the only built-in profile that terminates, and even then only once you have taken the daemon out of shadow mode. wallet still just notifies: it stops the spend by telling you, not by killing. For the full effective set including your own overrides, run cg profiles. It prints the config file it read on the first line.

Thresholds reload live. Edit the config, then, on POSIX:

cg reload      # SIGHUP: live sessions re-resolve thresholds, state preserved
cg status      # how close each session is to firing, right now
cg history     # what fired, when, and whether it acted
cg profiles    # effective thresholds per profile

Seeing that it is actually working

The commonest reason a watchdog gets uninstalled is that nobody can tell whether it is awake. Here are the two commands that answer that, with real output.

cg status shows what is running and how close it is to each threshold:

config: /home/you/.continuity-guard/config.toml
socket /run/continuity-guard/cg.sock   2 session(s)   12:29:53

SESSION                       PID   SEQ   QUIET   STUCK  TOK/MIN  REP  PROFILE   MODE
nightly-refactor           130847    26      1s      1s    19200    8  default   shadow
nightly-refactor           130889     8      2s      2s    19200    8  default   shadow

cg history shows what fired, when, and whether it acted:

config: /home/you/.continuity-guard/config.toml
WHEN                SESSION            DETECTOR  LEVEL ACTION   DETAIL
2026-08-29 12:30:02 nightly-refactor   repeat    warn  notify   repeated apply_patch(33d034ea) x13 (unconfirmed: 21s of 60s)  [shadow]
2026-08-29 12:29:41 nightly-refactor   repeat    warn  notify   repeated apply_patch(33d034ea) x3 (unconfirmed: 0s of 60s)  [shadow]

That second line contains the whole safety design. repeat saw the same call thirteen times. It said so at warn, marked itself unconfirmed: 21s of 60s, took the action notify, and tagged the record [shadow]. Nothing was killed. A hard verdict has to hold unbroken for its confirmation window before it escalates, and in shadow mode it never acts at all.

Both commands print the config file they read on the first line. That is not decoration. A stale ~/.continuity-guard/config.toml will quietly disagree with this page, and if you cannot tell what is configured you cannot trust what it will do.

The columns in cg status are the values the thresholds are compared against: quiet time, stuck time, tokens per minute, current repeat run. Tune against those rather than against guesses.


Plan windows, and not losing a run to one

On a subscription the expensive ceiling is time, not money. Exhaust a weekly window and you wait days, because there is nothing to top up. No provider publishes remaining headroom in advance, so nothing else in your stack knows it is coming.

Continuity Guard reads it from what the official clients already write locally and from what passes on the wire, then tells your sessions before it is too late.

cg quota
PROVIDER   WINDOW         USED  RESETS               AGE  STATUS
grok       weekly        63.0%  Sat 23:21            31m  ok
openai     primary        0.0%  Fri 17:15            38m  ok
           secondary      0.0%  Fri 12:15            38m  ok
anthropic  five_hour     43.0%  Fri 17:50            35m  ok
           seven_day     43.0%  Mon 02:00            35m  ok

Verified against each provider's own usage screen; see docs/quota-accuracy.md.

A stale reading is refused rather than trusted. Once, a Grok cache read 13.0% while the account was actually at 63%, from a file five days old. That is the most dangerous state a meter can be in, because it looks like knowledge. Any snapshot older than an hour is marked IGNORED and cannot trigger anything, and an unknown age counts as stale.

At wrapup_at, which defaults to 95% of the binding window, every live session is told to save its state. A handoff is written for each of them whether or not they respond. A wedged agent cannot act on the signal, and that is when the record is worth the most.

with guard("nightly-refactor", profile="overnight") as s:
    for step in agent.run():
        s.progress(...)
        if s.should_wrap_up():          # cheap, non-blocking
            write_handoff(s.headroom()) # what to save is yours to decide
            break

The handoff Continuity Guard writes itself is a record of what it observed: steps, tool calls, timings, tokens, the plan window at the time. Not a summary of intent; the agent did not write it. If your agent also acts on the signal you get a better one on top.

Ceiling signals go to the three ceiling conditions (a plan window, a budget, a deadline) and never to faults. Nothing is wrong with a session that is merely running out; a wedged one cannot answer anyway.

What the handoff is and is not

The obvious reading of "handoff" is too generous, so here is the honest version.

You always know what a run reached, including when the agent died without cooperating. Enumerable progress survives: which items are done, which remain, and the command that resumes past them. When the work is a list, and a great deal of agent work is a list, that is most of what you needed. The next run picks up without repeating itself.

What does not survive is the agent's understanding. The half-formed hypothesis, the reason approach A was abandoned, the thing it had noticed but not yet put into words: that lives in the model's context and goes when the context goes. Writing it down is lossy, and the loss is invisible in the result, because you cannot tell from a summary what is missing from it. This is the context-compaction problem and nothing here solves it.

Call it a warm start rather than a continuation. A successor reads the handoff and begins informed instead of blind. It does not resume mid-thought.

Two consequences are worth planning around.

Task shape decides how much this is worth to you. Enumerable, independent units hand off well. Exploratory debugging hands off badly, because its value was never in the enumeration; it was in the hypothesis space, which is the part that does not survive.

Findings also have a shelf life. In one measured case, two of four findings in a handoff had already been fixed a few hours later, and a third was wrong on its own terms. A document describing a moving codebase decays. Handoffs carry an observed-at stamp so a reader can judge for themselves, which is mitigation rather than a fix.


The socket is kill authority

An armed daemon terminates processes it is told about, and everything in a session's announcement is that peer's claim about itself. The socket is therefore a privileged surface and is treated as one. Four checks, each independent, because an operator will eventually widen one of them for a good reason:

  • The Unix socket is 0600. It used to be 0666. Set CG_SOCKET_MODE=660 with a shared group to widen it deliberately, when several accounts must genuinely share one daemon.
  • A session may tune its own thresholds but never its own action. Overrides arriving over the socket have operator-only keys stripped, so action comes from your config file or not at all. Whatever was dropped is logged.
  • Identity is verified rather than assumed. Terminating requires a start_time matching the live process. A missing one used to fall back to "is anything alive with this PID", which accepts every process on the box.
  • The announced PID must be the connecting peer or a descendant of it, since a supervisor announcing its child is the normal case. Anything else disarms termination for that session and says so in the log. Observation continues.

TCP is not authenticated. socket = "tcp://..." binds 127.0.0.1 by default and should stay there. There is no token and no TLS, and a reachable armed daemon is a kill primitive for anything its user can signal. Peer credentials do not exist over TCP, so termination is disarmed there deliberately.

The full threat model, and the disclosure of the local privilege issue fixed in 0.5.0, are in SECURITY.md.


Shadow mode

The daemon ships observe-only. With shadow = true it evaluates every contract and logs every action it would have taken, and kills nothing.

Run it that way against real workloads first, and measure your own false-positive rate. Nobody should grant kill authority to a new watchdog before they have seen that number, and shadow mode produces the only metric worth having: N sessions observed, zero healthy terminations.

python3 -m continuity_guard.daemon -c config.toml            # config decides
python3 -m continuity_guard.daemon -c config.toml --shadow   # force observe-only
python3 -m continuity_guard.daemon -c config.toml --armed    # force enforcement

Notifications

Every sink declares which levels and detectors it wants.

[[notifications]]
kind = "file"
path = "~/continuity-guard-alerts.log"
on   = ["warn", "hard", "dead"]

[[notifications]]
kind    = "exec"                         # event passed as CG_* env vars
command = "~/bin/cg-notify.sh"
on      = ["hard", "dead"]

[[notifications]]
kind = "webhook"
url  = "http://127.0.0.1:9000/cg"
on   = ["hard", "burn", "repeat"]

A broken sink is logged and swallowed. Notification failure must never stop the daemon doing its actual job.


Safety properties

PID reuse is guarded. A PID recorded an hour ago may belong to something else by the time you act on it, so every kill verifies PID and process start time as a matched pair and refuses if they disagree.

Children are not orphaned. Agent sessions spawn shells, tool calls and model servers. Termination signals the process group, so nothing is left holding ports, files or money.

Grace before force. SIGTERM, wait grace, then SIGKILL.

Sleep is not death. CLOCK_BOOTTIME and CLOCK_MONOTONIC are compared every tick. A machine that suspended re-arms its deadlines with a grace window rather than firing. After a gap you have no trustworthy information, and the right answer to "I don't know" is to re-observe.

Refractory is durable. Fires are suppressed per session::detector::level in SQLite, with exponential backoff, keyed per level so an escalation is never swallowed by the warning before it. It survives daemon restart, which matters because the restart is exactly the moment when every session looks freshly overdue at once.

Heartbeats are never fsynced. Only state transitions are persisted. Writing every progress frame to disk would dominate I/O, and wear out a Pi's SD card, for no benefit.

Kill authority is local, and only local. A remote observer cannot tell "the session died" from "I cannot currently reach the session". Silence over a network is evidence of silence, not of death. If a hosted tier ever exists it will send a deadman notification, which is a claim about what the observer knows, and it will never issue a kill. That makes split-brain structurally impossible rather than merely unlikely.

What this does not promise. Killing a process is not the same as making the outcome safe. If a session already fired an API call, sent a message or charged a card before it stalled, termination does not undo any of it. Whether a hard kill leaves clean resumable state is a property of your application, not of this watchdog.


Works with any agent

Four ways in, in order of how much you get for the effort. All feed the same twelve detectors.

1. The proxy. Zero code changes, any language, richest signals. Nearly every agent and local runtime speaks OpenAI chat-completions: Ollama, vLLM, llama.cpp, LM Studio, LiteLLM, OpenRouter, the xAI (Grok) API, grok-cli, Aider, OpenHands, CrewAI, AutoGen, LangGraph. Point the base URL at the proxy:

cg-proxy --upstream http://localhost:11434 --port 8111   # Ollama
cg-proxy --upstream https://api.x.ai      --port 8111    # Grok
export OPENAI_BASE_URL=http://127.0.0.1:8111/v1

The wire format carries exactly what the detectors want, with no guessing: usage.total_tokens → burn/budget, tool_calls[].function → repeat/cycle, HTTP status → errors, request cadence → silence/decay. Streaming works, and gives token-level liveness for free. Name sessions with an X-CG-Session header, or run one proxy per agent with --name.

Limitation, and it is real: the proxy sees model traffic, not the process. An agent that exited cleanly and one that hung look identical from there. Pair it with the supervisor or library for process lifecycle.

2. instrument_openai(). One line, for any framework using the openai SDK.

from continuity_guard.integrations import instrument_openai
client = instrument_openai(OpenAI(base_url=...), "my-agent", profile="overnight")

3. CallbackHandler. LangChain, LangGraph, and anything that copied that interface. Duck-typed: this package never imports LangChain.

from continuity_guard.integrations import CallbackHandler
with CallbackHandler("research-agent", profile="loose") as cb:
    graph.invoke(state, config={"callbacks": [cb]})

4. Tracker / @guarded. Your own loop.

from continuity_guard.integrations import guarded

@guarded("nightly-refactor", profile="overnight")
def run(task, cg=None):
    for step in agent(task):
        cg.step(tool=step.tool, args=step.args, tokens=step.tokens, ok=step.ok)

And for CLI agents that offer no hooks at all, cg wrap wraps the process and derives progress from output.

Accuracy

tests/bench_accuracy.py drives the real detector code against a simulated clock, which gets thousands of sessions in seconds where the realtime soak manages about a dozen in ten minutes. The healthy workloads are adversarial by design. Each one is a legitimate pattern chosen because it looks like a specific fault.

Healthy archetype Looks like Why it is legitimate
polling repeat re-reads the same status file forever, while progressing
pipeline cycle read → edit → test, a new file each pass
flaky errors 30% of tool calls genuinely fail (empty greps, 404s)
bursty decay fast bursts split by 4-minute thinking pauses
rate_limited decay periodic provider backoffs
build decay 22% of steps take 30–150s
long_tool silence single 20-minute test-suite runs

Current numbers, 3600 healthy + 4000 faulty sessions (default profile):

overall any-level FP   0.1%      hard-level FP   0.0%
loop 100%  cycle 100%  stall 100%  burn 100%  errors 100%
depth 100%  decay 100%  budget 100%  cold_freeze 100%  cold_stall 100%

Remaining warn-level noise (survivable, never terminates): build 0.3% via decay, flaky 0.3% via errors; 0.1% overall.

Against real traffic, not just the corpus

The numbers above come from an adversarial corpus: synthetic sessions with known ground truth, which is what makes false-positive rates measurable at all. That is the right instrument for tuning and the wrong one for the question anyone actually asks, which is whether it will kill their work.

So it also runs in shadow mode against real agent traffic, on two hosts (a Raspberry Pi 5 and an x86-64 NUC) across four provider paths: the claude CLI under cg wrap, and Grok, OpenRouter and local models through cg-proxy. Shadow mode records the verdict and takes no action, so every session is a free observation of what an armed daemon would have done.

As of 2026-08-29, since the harness fix in 63d4bc8:

observed healthy would have been terminated
real model sessions 629 475 0
including synthetic soak 3,470 0

Seven distinct real models. The one healthy session ever flagged in this pipeline (real-1787852493-clean, a Claude-Haiku repo-QA run) was a harness defect, not a detector one: the test loop's regex did not capture tool input, so every call hashed identically and repeat fired correctly on what it was shown. It is annotated in the ledger rather than deleted, and 63d4bc8 fixed the loop.

Be clear about what that table shows. It is a false-positive measurement and not a recall one. Faults in the real loop are injected against short sessions that often end before a detector's window elapses, so the catch rate there says nothing useful and is not quoted; recall comes from the corpus and the matrix, where the ground truth is exact.

The honest summary is that in roughly 630 real sessions, across seven models and four provider paths, an armed daemon would have terminated nothing healthy. The run is still accumulating, so treat that as a floor rather than a final number. It is also one operator's machines. It is not a substitute for measuring your own traffic before you arm anything.

Three things that only showed up at these sample sizes, all now fixed:

The decay hard level needed its own floor. When a session's learned baseline is small, floor dominates both decay levels and warn_k/hard_k collapse to the same threshold, so any qualifying slow run fires straight to hard. On build traffic that was a 1.2% hard-level false positive at --trials 200+ (invisible at 60). decay.hard_floor (default 200s) is now an absolute backstop above the slowest gap healthy heavy-tailed traffic produces; warn keeps the relative floor. Recall unchanged.

silence and decay need opposite statistics from the same data. Decay asks "has the typical pace collapsed?", which is a central quantile. Silence asks "how long may this agent legitimately go quiet?", which is the upper tail. Sharing one baseline made bursty workloads false-positive at 94%, because a median baseline calls the pauses anomalies when the pauses are the workload.

Before the first completed step there is no information at all. Firing a warning there is guessing, and it is how a 20-minute-per-call agent gets flagged in its first three minutes. Warnings are now suppressed until a session has produced one step; the hard ceiling still applies, widened.

Testing with agents

See docs/integrating.md for the safe wiring recipe per agent (and the instrumentation footguns to avoid), and TESTING.md for the full Pi runbook. In short:

./tests/run_matrix.sh                                   # 22 checks, ~14 min
python3 tools/soak.py --sessions 8 --duration 8h        # calibrate thresholds, free
cg wrap --name nightly -- claude -p "..."                # real CLI agent

tools/soak.py runs concurrent sessions with known ground truth and scores the daemon against reality, separating spurious notifications (noise) from spurious terminations (destroyed work). Ship with the second at zero.

cg wrap (also cg-supervise) wraps any CLI agent with no changes to it, registering the child's PID so termination reaches the agent rather than the wrapper.

Test matrix

./tests/run_matrix.sh — 22 checks (18 realtime, a protocol fuzz, a ceiling-signal check, an authority check, and a packaging check). Rows 1 and 5 are the ones that decide whether anyone trusts this: a watchdog that catches every stall but occasionally kills healthy work gets uninstalled after the first false positive, and deserves to be.

 1 clean completion              MUST NOT fire
 2 hard death (no retire)        socket EOF
 3 blackhole: alive but silent   SIGSTOP
 4 lying heartbeat               frames arrive, seq frozen
 5 legitimately slow work        MUST NOT fire
 6 agent loop                    identical tool+args
 7 token burn, no progress
 8 runaway recursion depth
 9 oscillation                   edit/test/revert, every call differs
10 retry storm                   tool calls failing
11 budget ceiling                spend WITH progress; burn must stay silent
12 wall-clock deadline
13 decay                         fast baseline, then a crawl
14 decay MUST NOT fire           uniformly slow but steady work
15 flap                          crash loop across sessions
16 refractory survives restart   no duplicate-fire storm
17 ARMED: group termination      children not orphaned
18 PID reuse guard               stale identity refused
19 protocol contract + fuzz      malformed/binary/partial/giant/seq/churn
20 ceiling signal reaches agent  client drain + budget/deadline delivery
21 authority + ceiling guarantees peer cannot make the daemon kill
22 installed wheel             can do what the docs say

demo_agent.py is the fault injector, one process that reproduces every mode on demand:

python3 demo_agent.py healthy|slow|loop|cycle|stall|burn|budget|decay|errors|deep|freeze|die|children [profile]

Install as a service

sudo mkdir -p /opt/continuity-guard /etc/continuity-guard
sudo cp -r continuity_guard /opt/continuity-guard/
sudo cp config.example.toml /etc/continuity-guard/config.toml
sudo cp systemd/continuity-guard.service /etc/systemd/system/
sudo systemctl enable --now continuity-guard
systemctl status continuity-guard

The unit sets Restart=always and OOMScoreAdjust=-500: the init system is what watches the watchdog, and the daemon must outlive the memory pressure that kills what it watches.


Status

v0.5.0, reference implementation. 22/22 on the matrix (18 realtime checks, a protocol fuzz, a ceiling-signal check, an authority check and a packaging check) on x86-64 Linux, Python 3.13. Raspberry Pi 5 (aarch64) and Python 3.12 are verified at 19/19, the matrix as it stood before checks #20 and #21 were added; those rows have not yet been run there. Accuracy on the adversarial corpus (default profile, 3600 healthy + 4000 faulty): hard-level false positives 0.0%, recall 100% on all ten faults; residual warn-level noise is build 0.3% (decay) and flaky 0.3% (errors), neither of which terminates. The announce socket is fuzzed against malformed, binary, oversized, type-confused and out-of-order input and the daemon stays up.

Pure Python so the contract semantics and thresholds can be validated fast on real workloads. The wire protocol (continuity_guard/protocol.py, plus sanitize() as its acceptance rule) is the durable artifact; the native daemon reimplements against it once the semantics are proven here.

See docs/ for the engineering history and the measurement behind each decision.

MIT. Ak2tx LLC.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

continuity_guard-0.5.0.tar.gz (262.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

continuity_guard-0.5.0-py3-none-any.whl (99.2 kB view details)

Uploaded Python 3

File details

Details for the file continuity_guard-0.5.0.tar.gz.

File metadata

  • Download URL: continuity_guard-0.5.0.tar.gz
  • Upload date:
  • Size: 262.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.5

File hashes

Hashes for continuity_guard-0.5.0.tar.gz
Algorithm Hash digest
SHA256 fbb02d3fe38236315da8f3f51d9a5831f6d153862ef9bac97750c34e5b6112d5
MD5 4d684b327cfe5e8af696de2146cd20d7
BLAKE2b-256 3a4391969a879047aed1453c3cd18433fd6be8e43b9b261073289c4b1f4a9227

See more details on using hashes here.

File details

Details for the file continuity_guard-0.5.0-py3-none-any.whl.

File metadata

File hashes

Hashes for continuity_guard-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b3bf27532bb92a134495f4ba6e1ce004946674f2da8df435a441965d383d657b
MD5 c43a4edc1c896ceec6b9b77eccfd3301
BLAKE2b-256 1dc1a253a0f7349afea2a97a3a159d66f09865d2f737c974286ec0a112bd2f11

See more details on using hashes here.

Release history Release notifications | RSS feed

0.5.3

2 files

0.5.2

2 files

0.5.1

2 files

This release

0.5.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page