Skip to content

Releases

antiRea v2.8.1

Choose a tag to compare

@dankezo dankezo released this 09 Nov 14:38

A patch release with one fix: a false-confirmation hole in the two phase signals, found live on the project's own test range the same day 2.8.0 shipped. No new modules, no API changes beyond one added result field, and no pre-existing test expectation changed.

  • Fixed - span gate for the phase signals (createHerdPhaseSignal, createActivityPhaseSignal): inside a fraction of one grid period (default 900 s), every population's event phases sit in one small arc, so R_i / R_cohort approach 1 by geometry alone and a short-window population could be falsely confirmed - on the antirea-range range, 12 legitimate readers were confirmed at R_cohort 1.0000 over a 40 s window. Confirmation now additionally requires each counting member's observed event span to cover at least one full grid period, and only eligible members are named; the new result field spanEligibleMembers reports how many members qualify. Descriptive statistics (memberCount, cohortR) are unchanged, and lab cells are unaffected because their spans cover many periods.
  • Tests: regression coverage in tests/phase-span-gate.test.cjs (5 cells: short-span legitimate, long-span legitimate, aligned-herd positive control, mixed spans, herd-phase core). Suite: 817 automated tests (815 passing, 0 failing, 2 skipped - pre-existing RSA-PSS vectors). The fix also passed the mandatory range release gate (antirea-range/gate.cjs: SDK suite + frozen FP/detection cells + live range smoke, 6/6 PASS).

antiRea v2.8.0

Choose a tag to compare

@dankezo dankezo released this 08 Nov 21:07

An additive, advisory-only release with five new modules: the combined room guard that 2.7.3 excluded at its frozen gate, rebuilt under a new pre-registered pin with the occupancy leg it lacked (RECOMB-06), and a four-module evidence layer ported from the neighbour-dissection candidates CS-02, BS-01, CAP-01 and KT-01, each past its own frozen Step 0. Nothing on the default path changes: no existing module imports a new one, and all 758 pre-existing tests pass unmodified.

  • Room guard - RECOMB-06 (createRoomGuard, NEW, advisory, population-only): pressure v2 over 30 s ticks - the original three paths (burst: join velocity vs the room's own trailing baseline with newness and inactivity legs; sustained: 30-minute concentration; churn: fast-leave cycling) OR the occupancy leg declared up front in the new registration (estimand and FAST 0.50/600 s + DRIP 0.34/3,600 s laws verbatim from createSeatOccupancySignal). One gate change declared before any number existed: the squat gate is 900 s, taken from CAND-273-1 for the same statistic, because its physical floor is ~660-720 s; the results file records the descriptive 600 s comparison openly (690 s does not meet it). Lab record (RECOMB-06, seeded simulation, 20,161 ticks per cell): pressure never high in five legitimate cells (steady, raid of existing accounts, scheduled showtime, karaoke prime time, and KARAOKE-SILENT with 60% of seat sessions silent, old and new; legitimate occupancy peaked 0.02-0.10 against the 0.34 DRIP threshold); FLOOD high at +60 s (gate 120 s), DRIP at +1,950 s (gate 45 min), CHURN at +30 s (gate 15 min) via the original paths; SQUAT high at +690 s (gate 900 s) via the occupancy leg alone, the original paths silent at every tick; SQUAT-DRIP flagged by the DRIP law 5,100 s before the last bot seated (gate >= 1,800 s of warning). Output is a room state plus recommendStepUpForNewJoins for NEW joins only - no output names an identity and seated members never receive a consequence. Declared open (registration, header, LIMITATIONS, and a pinning unit test): hole A1 - a squatter emitting even 1-2 activity events escapes the occupancy leg's zero-activity definition.
  • Config provenance - CS-02 (createConfigProvenance, configProvenanceHash, canonicalSerializeConfig, resolveEffectiveConfig, NEW): pins a signal's effective configuration to a SHA-256 hash over a canonical serialization of { kind, version, options } (defaults resolved, undeclared fields excluded, keys sorted at every level, -0 normalized, unhashable values throw). Lab record (Step 0 probe over the 43 declared options of the 7 kinds shipped in 2.7.4): cross-process determinism 100%, default-equivalence 100%, mutation sensitivity 100%, 0 collisions, ~0.005 ms per pin against a 1 ms gate. The shipped port reproduces the probe's seat-occupancy digest byte-for-byte (pinned as a test vector).
  • Verified-neutral facts - BS-01 (classifyWebBotAuthVerdict, classifyHostVerificationFact, NEW): the frozen semantics of identity verification as code. Over the shipped verifier's outcome space (17 failure reasons extracted mechanically from source, plus FCrDNS and published-range channels - 26 rows), verified, no-claim and every unverified outcome carry an evidence delta of exactly 0; only two spoof rows carry "EVIDENCE": the conclusive cryptographic invalid_signature detail and an FCrDNS contradiction. replay_detected surfaces as a side event, not an identity fact; unknown reasons and future detail strings default to unverified, so hosts never key enforcement on a detail string. Facts optionally carry the CS-02 hash of the producing verifier's configuration (the BS-01 spec hook). Lab record: conformance exact on all 26 rows, spoof set exactly 2, delta 0 on both the verified and unverified sides.
  • Step-up proof - CAP-01 (issueStepUpProof, createStepUpProofVerifier, stepUpChallengeHash, NEW): a three-part Ed25519 token binding one identity, the CS-02 hash of the configuration whose step-up it answers, the hash of the host's challenge config, a single-use nonce, and a lifetime capped at 3,600 s (60 s verification skew). The verifier requires a nonce-consume hook at construction and calls it exactly once per token that reaches the replay step - after structure, key, signature, claims, temporal and binding checks, never earlier. Lab record: 19/19 conformance cases exact, the hook firing exactly 4 times across the suite; the shipped port re-runs that law as unit tests.
  • Evidentiary roles - KT-01 (evidentiaryRoleOf, auditEvidencePack, decorateSignalResult, EVIDENTIARY_ROLE_TABLE, ENFORCEMENT_TIER_EVIDENCE_RULES, NEW): every evidence channel's role written as law - CONVICTING only with its own passed gates, otherwise CORROBORATING; the BS-01 fact is the one conditional row (spoofed convicts, anything else corroborates). room-guard joins the shipped signals as CONVICTING under the same rule via RECOMB-06. Pack audit is mechanical: an unknown channel or a mis-claimed role is invalid, and a pack with no fired CONVICTING item is ineligible for consequences however many corroborating items fire. Lab record: role table complete over all shipped kinds and pin channels, decoration fidelity verified against the real factories' evaluate() outputs, audit invariant exact on 10/10 frozen packs, tier restatement covering tiers 0-3 with tier 3 forbidden for inferred evidence.
  • Not in this release: the C27+C35 pin conversion (advisory to tiered consequences) remains behind the 2026-11-16 farm gate and a production stratum-assignment mechanism - the evidence layer above is its foundation, not its activation. Five dissection candidates (CS-01, CS-03, AN-01, AN-02, AN-03) have not run their Step 0 and ship in no form.
  • Suite: 812 tests (810 pass, 0 fail, 2 pre-existing RSA-PSS skips), up from 758 in 2.7.4 - 35 new tests across the five modules at release, plus a 19-test verification battery added the same day (full RECOMB-06 lab-population replay through the shipped guard, property tests, evidence-pipeline integration and BS-01/KT-01 drift guards), zero pre-existing expectations changed.

v2.7.4

Choose a tag to compare

@dankezo dankezo released this 07 Nov 12:01

New in v2.7.4

The short version: one new advisory signal joins the herd family. Where herd-phase (v2.7.3) asks whether a fleet's members share one timer offset, herd-tightness asks whether a whole group's timing points sit suspiciously on top of each other - the shape a single scheduler leaves on an entire cohort at once. Nothing on the default path changes.

  • One scheduler, one tight knot of points. Feed the new createHerdTightnessSignal the same repeated timestamps you already feed herd-phase (session starts, room joins), plus a reference stream from your established legitimate users. Each account becomes one point on a phase map; a fleet driven by one scheduler lands all its points in one tight knot, while real people - including a whole office behind one IP on office hours - spread out. In the pre-registered lab, a single frozen bar separated the two on two fresh held-out populations it had never seen: the shared-scheduler herd read 0.26-0.28 against the bar 0.81, legitimate users 1.09-1.36, the office-hours group 1.39-1.72, smallest margin 35.9%. The shipped module was then re-measured on the lab's own populations and reproduced every value to the last printed digit.
  • What it refuses to do. The statistic describes a whole group, never a person: no output names an account. A fleet whose bots each keep their own timer reads spread out and is not flagged - that shape belongs to herd-phase's cohort statistic, not this one; the two signals are companions, not substitutes. And without a reference stream the module issues no verdict at all: it reports the raw number with status "uncalibrated" rather than guessing. Advisory only, like everything in this tier - evidence for an accumulator or a human, never a block.
  • The first design failed, on the record. The candidate's first registered form filtered "outlier" points before measuring - and the filter deleted the entire bot cloud on the calibration draw, because a tight herd sitting far from the legitimate centre looks like outliers. That run was void; the filter was removed, not loosened; the re-registered form is the one that passed. The full story, with numbers, is in the deep dive.

Full technical write-up: v2.7.4 deep dive. What the previous release changed: v2.7.3 deep dive.

v2.7.3

Choose a tag to compare

@dankezo dankezo released this 06 Nov 08:26

What's new

Six new opt-in, advisory-only signals for live watch-together / streaming rooms:

  • createHerdPhaseSignal: detects fleets of accounts joining on a shared schedule
  • createSeatOccupancySignal: detects fresh accounts squatting seats while idle (population-level, names nobody)
  • createActivityPhaseSignal: shared-schedule detection for chat and activity streams
  • createVoteBrigadingSignal: detects coordinated skip-vote brigades by new accounts
  • createRoomHopSignal: flags identities hopping across many rooms per hour
  • createReconnectStormSignal: flags single-source reconnect storms

Also new: docs/AGENT-GUIDE.md, a wiring guide written for the AI agents of vibe coders, with a symptom-to-module decision tree and copy-paste examples.

The default protection path is unchanged. All new modules are opt-in and report only (enforcement: "none"); they never block anyone by themselves.

Honest note: there is no combined createRoomGuard in this release. Seat-squat coverage ships as the seat-occupancy population signal. See CHANGELOG.md and docs/LIMITATIONS.md for measured numbers and known blind spots.

748 automated tests (746 passing, 2 skipped).

v2.7.2

Choose a tag to compare

@dankezo dankezo released this 04 Nov 03:20

An additive, advisory-only release: five lab-validated signals for the slow shapes join the opt-in tier, each re-measured on the shipped implementation with fresh seeds before inclusion. Nothing on the default path changes - no existing module imports the new ones, all 621 pre-existing tests pass unmodified, and the classic release round re-measured every cell against the 2.7.1 baseline with NO new regressions. One further candidate was excluded at the SDK stage (see the last bullet).

  • Shrink-slope signal — C27 (createShrinkSlopeSignal, NEW, advisory): a per-identity empirical-Bayes detector for the persistent slow harvester. It estimates θ - the mean new records per day over an identity's own days 31-90 - under a robust mixture prior fitted per request-volume stratum on a host-supplied reference population, flagging when P(θ > c_s | data) >= 0.975 with c_s the stratum's reference q99. Lab record (pre-registered, seeded simulation): 0/1,200 false flags on legitimate and 0/1,200 on sale populations, steady plateaus at 7/20/50 new records a day flagged 300/300 each, a 7-to-20 ramp flagged 300/300, prior contamination at 5%/15% held false flags at 0.00%. The SDK re-measurement on fresh seeds reproduced every cell exactly (0/1,200; 0/1,200; 300/300/300; 300/300) at 0.003 ms per identity scored. One translation deviation, quantified in the recheck rather than absorbed: the port scores on an identity-relative day clock and takes the host's "now" through evaluateIdentity(identity, asOfTs) - a draft recheck run without it left 312/1,200 legitimate identities at 'insufficient_history', with asOfTs supplied past every identity's day 90, every recheck cell scored every identity (0 unscored). Declared structural blind spots, pinned by tests: identities younger than the late window are never scored (cold start is the budget layer's ground), and a front-load-then-idle shape is invisible to the late-window estimand (0/300). The perfect separation is a property of the lab's saturating legitimate generator - see LIMITATIONS for the unmeasured real-reader shape.
  • Watchlist signal — C30 (createWatchlistSignal, NEW, advisory): a per-identity regularity statistic for machine-paced revisiting. For each resource visited on >= 5 distinct days it computes the coefficient of variation of the visit-day gaps; the identity's statistic is the median across its >= 3 eligible resources, flagging at median CV <= τau (default 0.613, the lab TRAIN q01; hosts can refit from their own reference traces). Lab record: false flags 1.33% legitimate / 0.83% sale; the SDK re-measurement on fresh seeds measured 1.67% legitimate (20/1,200) / 1.17% sale (14/1,200) - both under the locked 2% bar - flagged unjittered pollers 120/120 and jittered pollers 120/120 (jittering every interval by up to ±100% at 100%. The declared false-flag groups are habitual same-resource checkers (SDK 5.42%; lab 4.83%) and event-driven checkers (SDK 5.33%; lab 4.50%) - near-periodic humans are a named cost of the day scale, and sub-day polling is invisible by construction. The port's gap-CV divides by the population count exactly as the lab code does; no sd-convention deviation exists.
  • Decoy tripwire — C35 (createDecoyTripwire, NEW, advisory): a touch tripwire over host-declared decoy resources - resources planted outside every discovery surface (no navigation, sitemap, listing or search index). A touch is reported as an enumeration event ('flagged = touched >= 1'), never as an intent verdict. With random placement - verified in the lab as the standard configuration and shipped as such - enumeration walkers touched a decoy in 100% of cells at every sweep size and order (540/540 walker cells in the SDK re-measurement), a learning evader still touched in 100% (60/60 at every sweep size), and legitimate browsing touched none (0/1,200 identities over 1,116,264 requests on the SDK re-measurement; 0/1,200 over 1,115,408 requests in the lab). With no decoy declaration the module disables itself and records nothing. The signal dies if a decoy leaks onto any discovery surface; that operating condition is part of the module's contract, stated in its header and in LIMITATIONS.
  • Bunching monitor — C29 (createBunchingMonitor, NEW, advisory): a population-level calibration gauge for extraction that piles up just under a ceiling the host enforces. It reads the population's daily-novelty histogram and measures excess mass in the window below a host-known notch against a degree-5 polynomial counterfactual on log counts fitted outside an [n-3, n+2] donut, with a parametric-bootstrap standard error (200 Poisson draws from the smoothed fit). Lab record (pre-registered, seeded simulation): placebo z <= 2.17 across notches (10, 20, 25, 50) on legitimate and sale populations with a fresh-seed placebo maximum of 1.55, and a planted hard-calibrator control at z = 36.97, and the excess mass migrating to the new notch when the ceiling moved (z = 29.47). The SDK re-measurement on fresh seeds reproduced the verdict: placebo maximum z = 2.05 with no cell at or above the locked +3 bar across 24 legitimate/sale cells, and the control and migration cells matched the lab exactly (36.97 / 29.47); feeding the lab's cached legitimate histogram into the module reproduces its z/B/SE to every printed digit. The advice ceiling is structural, not a promise: the module has no identity-level API and no per-identity verdict field anywhere in its output - it reads population histograms only - populations under minIdentityDays (default 2,000 identity-days) are reported unscored, and the notch must be an integer in [4, 60]. Declared limits from the lab record: the measured control is a HARD calibrator (soft, probabilistic calibration is unmeasured), the estimator's sensitivity to polynomial and donut choices is declared but was never quantified, and a threshold-blind slow walker does not bunch by construction.
  • Value watermark — C39 (createValueWatermark, NEW, advisory): a provenance verifier for derived totals a host publishes (per-edition feeds, exports, partner reports). At publication time each total is marked with a keyed residue embedded by magnitude (units digit always; tens and hundreds digits on larger tiers; distortion on the shipped marker measured at median 0.260%, p99 2.34% of the total - lab: median 0.229%, p99 2.18%); verification decodes the residue from (quantity, unit price, published total) triples and confirms a scale when its matches clear the adaptive R-ADAPT bar, the edition verdict being the OR across scales - so the mark survives #1 noise (the hundreds scale confirms) and rounding to the nearest hundred (the tens/unit scales confirm) without either transformation being anticipated. SDK re-measurement on fresh seeds: false confirmations 0/200 exact, 0/200 rounded, 0/200 cross-key; marked editions confirmed 200/200 under ±1 noise, 200/200 under rounding, 200/200 through a two-step transformation chain; group totals are covered by the module's unit tests (50/50 marked groups confirm, honest groups do not) rather than by a recheck cell; small marked editions that cannot confirm alone confirm pooled across 80 editions at 90.0% (180/200; lab 94.0%) - exactly at the pre-registered >= 90% bar, a thin pass recorded as such. Structural properties, stated up front: totals recomputed from their components carry no mark (0/200, pinned by test), and a fully informed adversary who knows the key and placement can forge or strip marks, so the claim is provenance of the publication path, not resistance to an insider.
  • Excluded at the SDK stage — lineage watch (R14): the lab candidate for grouping synthetic account lineages holds a PASS-lab record (the v2b law, stability-verified over 5 fresh seed blocks: pooled family recovery ARI 0.782, flat false-merge <= 0.614% per block), and its SDK port's clustering is bit-faithful - at any fixed threshold its partition is identical to the lab's. The port nevertheless failed the locked SDK recheck: its self-calibration draws nulls by permuting coordinates of the OBSERVED population, and on a population that HAS lineage structure those marginals are lumpy with founder mass, so the calibration false-merge rate at a given threshold runs higher than under the lab's generator-drawn flat nulls, the <= 1% selection rule picks a lower cut (h_b 0.24-0.28 on the structured cells, against the lab's 0.32), and recovery pays for it - pooled CLEAN ARI 0.622 against the locked 0.75 bar and EV-SWITCH 0.439 against 0.50 (EV-MERGE 0.527 and flat false-merge 0.645% passed). On flat populations the two nulls nearly coincide and the deviation vanishes, which is the evidence the defect sits in the calibration step, not the clustering. Under the release rule - no gate loosened after the numbers, no candidate forced in - it does not ship in 2.7.2. The full record is in the deep dive (Part 7); any fix is a change to the calibration mechanism and requires its own registration, not a patch in this release.

v2.7.1

Choose a tag to compare

@dankezo dankezo released this 03 Nov 12:51

The short version: three research signals that spent the autumn in the lab - each had to pass false-alarm and detection bars fixed before it ran - join the opt-in advisory tier. Nothing on the default path changes, and the classic release round re-measured exactly that.

  • Crews that work in shifts. The cohort box signal watches a whole team's traffic pooled together and looks for a repeating on/off rhythm - a crew scraping in 36- or 48-hour rotations. In the lab it flagged rotating crews 9 of 9 (36 h) and 8 of 9 (48 h) with 0 false alarms across 250 legitimate group tests, sale weeks included. It judges the group, never a person - and if your own team works declared shifts, you declare the roster and it stands down.
  • Swarms of one-use accounts. The missing-mass signal watches each area of your site instead of each visitor: when a crowd of accounts each shows up exactly once and never returns, the share of one-time visitors jumps. In the lab it caught herds of 100-400 one-use accounts at 100% with a 0.36% legitimate flag rate, and sale weeks stayed clean (0 of 140). A swarm disciplined enough to reuse every account still escapes this one signal - it hands itself to the other meters instead, a trade-off designed and recorded before the lab ran.
  • Accounts aged, then spent all at once. The lifecycle cohort signal ignores the quiet aging phase and watches the wake-up: old accounts returning together, bursting far above their own history, in sync. Legitimate comebacks - a product launch pulling old users back - stayed clean in the lab (0 false alarms in 210 group tests) while the aged-then-burst crew was flagged 18 of 18. Its per-account score exists too, but the lab measured it flagging too many innocents (2.9%), so it ships as description only - there is deliberately no per-account verdict to wire up.
  • Honest limits, printed on the box. A crew rotating exactly with the day is invisible to the shift signal; strangers who view one deep page and leave (a viral link) were never measured on real logs; and all three signals are simulation numbers until a real host calibrates them - the farm calibration gate (~16 Nov) is unchanged. None of the three blocks anything: each only reports.
  • The default path, re-measured. The full classic round ran again against this build: the export hammer still stops at 16 of 80 records, declared teams and ordinary users still see zero friction, and every number matches the previous release - no new regressions.

Full technical write-up with every mechanism and measurement: v2.7.1 deep dive. What the previous release changed: v2.7.0 deep dive.

Automated tests: 621 total, 619 pass, 0 fail, 2 skip (RSA-PSS Web Bot Auth vectors, Ed25519-only by design).

v2.7.0

Choose a tag to compare

@dankezo dankezo released this 03 Nov 04:49

New in v2.7.0

The short version: the alarm system used to rely on thresholds fitted in a lab - good numbers, but no promise behind them. v2.7.0 adds an evidence engine with a mathematical false-alarm promise, two new advisory signals, and a hardening merge underneath.

  • An alarm with a promise attached. The new e-value tripwire (opt-in) works like a careful gambler's bankroll: if your legitimate users stay under an intensity bound you set, the chance it ever fires falsely is at most the level you picked (say 5%) - guaranteed by construction. In 10,000 simulated legitimate runs it fired 0 times; at the hardest honest boundary, 4.2% against the 5% promise. It caught slicing at a median of 400 records vs 1,225 for the current engine.
  • A second shape for quiet-then-loud attacks. The plain bankroll reacts slowly after a long quiet spell, so a Shiryaev-Roberts mode ships alongside: median 14 steps to catch an attack after the quiet period, vs 774 - under its own, different guarantee, stated plainly. A calibration helper measures the bound from your own traffic.
  • The honest catch, printed on the box. The promise is exactly as good as your bound: a legitimate workflow running hotter than the bound will eventually trip it - hot-but-honest jobs like bulk importers belong in the declared lanes or need their own bound.
  • Hard ceilings, exact in a crowd. Forty workers hammering a declared cap of 600 records overshot to 1,575 on the old check-then-serve path. The new reserve-first path holds exactly 600, zero overshoot - and its holds now expire on a monotonic clock, so a system-clock jump can't stretch or kill them.
  • A new signal for feature enumeration (advisory). Input-space coverage watches which features and resources an account touches, not just how much data it takes. In the lab it flags sweep and random-probe enumeration 9 of 9 at the first checkpoint (the current engine: 0 of 9) and a work-splitting herd 72 of 72 members - at a 1.15% legitimate flag rate. It only scores what you declare, and it never blocks anything by itself.
  • A new signal for crawler-shaped browsers (advisory). The asset/referrer profile notices browsers that never load images and never carry referrers: 0 false flags in the lab (6,658 legitimate streams, 923 sale-week streams), crawlers flagged 9 of 9 - and shipped deliberately slower-but-surer, needing two bad windows in a row before it flags.
  • Verified users stop getting re-challenged. Pass a step-up check once and a new grace window (24 h by default) leaves you alone at ordinary evidence levels. It breaks instantly if evidence jumps to twice the flag line - and it never lifts any budget or ceiling.
  • A real bug found and fixed. New heavy-search attack tests caught a bystander being swept into an attack group's flag - worse, the accident shielded one attacker from friction. The grouping rule is fixed: bystander clean, all 16 attackers enrolled.
  • Signed-bot verification, re-checked. An independent conformance pack for the RFC 9421 verifier, built from the specification's own example keys: all 12 broken signature variants rejected, false accepts 0.
  • Your policy, in the formats agents read. A small generator emits your data-use policy as robots.txt signals, a TDM reservation file and a policy JSON - correct format only, no enforcement claimed.
  • All recent hardening, merged in. Every fix shipped in the 2.6.1-2.6.3 patches (idempotency binding, atomic quota enforcement, priced CSV exports, tamper-proof clocks, and more) is part of this line - a pre-release audit caught that the 2.7 development tree had forked before them, and nothing shipped until they were merged back and re-verified.

Suite: 585 tests (583 pass, 0 fail, 2 skipped). Full technical write-up: docs/v2.7.0-deep-dive.md

v2.6.3

Choose a tag to compare

@dankezo dankezo released this 02 Nov 17:38

A patch release that fixes the eight medium-severity issues found by the independent audit of v2.6.0. No new features and no upgrade steps - the same limits you already configured are now enforced in a few places that used to have gaps.

  • Quota and export ceilings are now enforced atomically with the bundled store. Before, a slow store let parallel requests slip past each other and overshoot the cap. Checking and recording is now a single step.
  • CSV exports are now charged like JSON exports. A CSV body used to price at zero by accident - in a live test it pulled 150 records through a ceiling of 60. Now every export format pays the same way.
  • An action with no declared price is refused instead of free. If your app forgot to price an action, it used to run free forever. Now it is refused until it has a price (or you set a fallback price).
  • A crashing host callback no longer loses a warning flag. If your notification handler threw an error, the flag used to vanish. Now it is kept and delivered again at the next touch.
  • A NaN input no longer locks a user group forever. One malformed number used to poison a group's counter until restart. Now it is refused on the spot and the counter heals itself.
  • Tracking memory is capped and cleans itself up. Per-identity tracking state now has a size limit and evicts the least active entries, instead of growing forever.
  • Calibration rotation now really resets scores. When an evidence window rolls over, old scores and contributions are actually cleared - before, they lingered and could re-flag a group instantly.
  • The test suite grew to prove it: 338 tests, 336 passing, including 16 new tests that all fail on the pre-patch code.

Full technical write-up: v2.6.3 deep dive. The clock hardening in v2.6.2 is covered in the v2.6.2 deep dive and the changelog.

v2.6.2

Choose a tag to compare

@dankezo dankezo released this 02 Nov 16:47

A small patch release about clocks. antiRea's time windows used to be measured with the server's wall clock - the one that can jump when the machine syncs its time or an admin changes it. Those jumps could bend your limits. They no longer can. Nothing else changes: no upgrade steps, no new settings.

  • A clock jump forward no longer hands out a double budget. If the server clock leapt 5 minutes ahead, a spent 60-second window looked "expired", so one account could take 200 records in 10 real seconds - double its budget. Measured in the lab on the previous version; now the window only counts real elapsed time and the extra batch is refused.
  • A clock jump backward no longer locks out honest users. The same leap in the other direction kept a finished window "closed" for 5 extra minutes, blocking normal users for no reason. Now windows end on schedule.
  • Real deadlines still use the real clock. Signed links that expire at a set time, daily caps and calendar rules keep working exactly as before - only the stopwatch-style timers changed.
  • The test suite grew to prove it: 322 tests, 320 passing, including new tests that replay both clock jumps against every timer in the package.

Full technical write-up: v2.6.2 deep dive. The three audit fixes in v2.6.1 are covered in the v2.6.1 deep dive and the changelog.

v2.6.1

Choose a tag to compare

@dankezo dankezo released this 02 Nov 16:01

A small patch release: an independent security audit of v2.6.0 found three bugs, and all three are fixed. Nothing about how antiRea works changes for normal users - no upgrade steps, no new settings.

  • A borrowed "receipt number" no longer works. Every reservation carries an idempotency key - think of it as a receipt number for "I already paid for this". Before, anyone who learned another account's receipt number could present it as their own and spend from that account's budget. Now the receipt is checked against what it was first issued for, on both ledgers; a mismatch is refused, and reservations written by older versions (which carry no fingerprint yet) fail closed for the few minutes they have left to live.
  • A broken database can no longer crash your app. If the storage behind the ledgers broke or returned garbage, the SDK used to throw raw errors into your request handling - in the audit's live test, a dead store killed the host process on its very first request. Now the SDK answers with a clean refusal instead (an HTTP 429 "store unavailable" style decision) and your process stays up.
  • Huge record IDs are cheap again. The ledger used to keep a checklist with one box for every possible ID, so a single record with ID 100,000,000 bloated the saved state to 16.7 MB. It now keeps a sparse checklist that only grows with the number of records you actually have: that same case takes 3 ms and 1,745 bytes of state (was 1,439 ms), and states saved by older versions still load.

Full technical write-up: v2.6.1 deep dive (docs/v2.6.1-deep-dive.md in this repo).