Skip to content

Replicant v0.4.0

Choose a tag to compare

@404SecNotFound 404SecNotFound released this 01 Aug 15:07
· 101 commits to main since this release
b19b080

Two things a live LogRhythm test found, and neither was a bug in what Replicant generates. The events were always correct. When they arrived, and whether the interface could be read, were not.

Upgrade note: live sends now take as long as the plan says

This is a behaviour change, and it is the reason for a minor bump rather than a patch. replicant run REP-001 --host 10.20.0.50 used to return in about three seconds. It now takes 238 minutes, because that is how long the beacon it describes actually lasts.

Nothing was removed and nothing errors. Every interface states the projected duration before it starts, and --pace burst restores the old behaviour exactly. Scripted callers that only need to prove a socket works should say --pace burst explicitly: that is what the installer's own loopback check now does.

Fixed: events are sent when the plan says they happen

Measured against a live collector: REP-001 --intensity low delivered 49 events in about 3 seconds, carrying event times spread over 238 minutes.

The plan has always held a per-event eventtime. The emit loop ignored it and fired as fast as the rate cap allowed, so what reached the SIEM was a snapshot claiming to be four hours of history. No interval-keyed detection can work on that: a beacon rule asking for N callbacks at a regular interval over M minutes sees every callback at once, so it either never fires or fires on the wrong shape.

--pace {burst,plan} and --speed N, on the CLI, the scenario runner, the Rich menu and the web run form.

  • plan reproduces the gaps the plan's own timeline holds. The default whenever events go to a collector.
  • burst is the previous behaviour, kept and defaulted for --to-file, where the wall clock means nothing.
  • --speed N compresses the timeline, event times included.

Compressing only the schedule would have re-created the original defect at 1/60 scale: events stamped 238 minutes ahead, delivered inside four. Moving both holds one invariant at any speed:

With --pace plan and --anchor now, an event is sent at the moment its own timestamp says it happened.

Verified over a real socket rather than in a test. A 14280 second plan at 1428x delivered 49 datagrams over 10.0s, carrying a 10s event-time span, with the furthest event time 0 seconds into the future.

The cost is stated rather than buried: compression preserves relative timing and changes absolute intervals, so a rule keyed on five minute gaps will not match a run compressed 60x. Real time to validate a rule, compressed for a smoke test.

--rate is unchanged and composes rather than competes. It stays the events-per-second flood guard and enters the schedule as a floor on spacing, so a plan holding several events in one second still cannot deliver them together. The two answer different questions and are kept apart in the UI.

Added: the choice is a control, not a flag you have to know about

POST /api/plan prices a run without starting it, so the web form can put each option's real duration on the option itself:

Pacing   (•) Plan time 3h 58m    ( ) Burst 0.2s
Sends each event when the plan says it happens. This run takes 3h 58m,
and event time matches send time throughout.

Radio buttons rather than a dropdown: a dropdown shows one option and hides the other, and the comparison is the entire value. The Rich menu asks the same question on a live send and prices the run before "Start run?". The CLI prints the projection before the first event.

Every run manifest now records pace and speed. The same seed and technique can put very different shapes on the wire, so the shape is part of the audit record.

Added: a light theme, and a responsive layout

Present in main since 29 July and unreleased until now.

  • The UI follows prefers-color-scheme on first load and remembers an explicit toggle after that.
  • Warm paper rather than cold white, with contrast measured pair by pair against the dark theme's own ratios. That audit found four defects in the shipped dark theme, including body text at 2.78:1.
  • Below 1024px the fixed-viewport shell becomes an ordinary scrolling page and the left rail becomes a disclosure.
  • The embedded terminal resolves its colours from the stylesheet and recolours in place, rather than tearing down the websocket and killing a running menu process.

Verification

691 Python tests and 118 frontend tests, black / ruff / mypy / shellcheck / tsc -b clean. All 10 CI jobs green on the tagged commit.

The scheduling arithmetic is asserted purely, with no clock and no sleeping. The delivered shape is asserted on observed send times against planned gaps, because the claim is about what a collector sees. End to end over a real socket: 5s of event time delivered in 0.17s under burst, over 5.04s under plan, and the no-flag default matching plan exactly.

Known limitation

eventtime is integer epoch seconds, so one second is the finest gap a plan can express and a hard ceiling on useful compression. Past roughly the plan's own gap size, every event collapses into the same second.

Replicant generates synthetic telemetry for detection engineering in a lab. It writes log text and never executes attacks.