Settings has six tabs: General, API keys, Proxies, Notifications, AI and
Users. Each tab is one card whose controls save on change; list tabs are
tables with a row menu and their Add action in the tab row. A non-admin
sees General and AI, read-only.
Out-of-band testing is the Callback server sheet on the Arsenal Nuclei
tab, with a Need a callback count that filters the library to the same
rows. Bounty Hub settings are a sheet on the Bounty Hub page. The old
settings routes redirect to both.
- API keys store the last test result on the row and clear it when the
key changes. Providers report whether a test exists. The usage count
is gone: only ViewDNS ever incremented it.
- Notification channels store the last delivery, written by the
notifier on its own connection. A channel offers the seven event
types the product emits, program changes on by default, and levels
named All events, Warning and critical, Critical only.
- A default proxy applies to a scan launched without a context and is
preselected for new contexts. A pool hands each scan one endpoint,
proxy URLs bracket IPv6 hosts, and deleting a proxy clears it from
its contexts.
- Users can be added with a role, disabled and promoted. An account
cannot change itself, and one that owns records answers 409.
- Retention windows are validated and include keep forever. The
instance name cannot be blank and names every page title.
- The AI list drops the two switches nothing read; each feature states
what it sends. The out-of-band Off help says the checks are skipped.
Connectors, Bounty Hub and Remote control lose their extra page
padding. Migration 5d2e9a8c1b47 adds the test and delivery columns and
drops proxies.mode.
Claude-Session: https://claude.ai/code/session_01RVAHQxZ7JAkoEiTCDiHYue
Every provider carries a group: subdomain sources, lookups, exploit
intelligence, bug bounty platforms, out-of-band testing and chat. The
labels are one map in shared/definitions/api_keys.py with a mirror in
config/api-keys.ts, and the provider list returns the group and its
label. The Telegram provider and the shared notification bot are
mirrored on the frontend, and a grammar test pins all three mirrors.
Claude-Session: https://claude.ai/code/session_01RVAHQxZ7JAkoEiTCDiHYue
Second review round. The fingerprinted matched tier now runs before the
known-exploited sweep, so a budget cut lands on the broad sweep and the
lower-yield tiers, never on the high-yield product checks. The blind
budget is sized from both lane rates and floored at zero rather than a
per-check cap.
The "not reached within the time budget" sentence is one shared constant;
the DAST stage now reports a budget cut as partial instead of success.
Rate and concurrency overrides are accepted only for tools that have a
rate, matching the builder. The rates panel no longer marks the engine
unsaved when a value returns to the preset. Narrating comments removed.
Claude-Session: https://claude.ai/code/session_01Bb4u8p34euie1YFzb7qCPu
The built-in engine drops subfinder-and-crtname-only discovery for the
stage defaults plus the nuclei scan. Port scan, zone transfer and
bruteforce default off; the passive tool set is subfinder and crtname.
worker-scans gets a 512m /dev/shm, and the unused MCP capability-chips
component is removed.
Claude-Session: https://claude.ai/code/session_01Bb4u8p34euie1YFzb7qCPu
http_probe and origin_probe now read the httpx stall flag after the stream
closes, so an idle kill is reported instead of passing as success; httpx
runs with -stream so the input shuffle controls request order, the idle
floor is back at 300s and the probe chunk is 2000.
The screenshot stage ranks every live web asset (answering, hostname,
https first), counts only readable renders, deletes the empty files,
reports failures and folds the best render per host onto the subdomain.
nuclei runs the two cheapest broad tiers, then the known-exploited sweep
sized to the time left, then the rest; the blind and deep tiers share one
budget so no check is stranded between them; a budget cut writes a
coverage row and a warning without inflating the check count or letting
the deep tail change the verdict. git, svn and dotenv exposures move into
the universal tier.
Claude-Session: https://claude.ai/code/session_01Bb4u8p34euie1YFzb7qCPu
An engine keeps its three intensity presets and now carries optional
per-tool rate and concurrency in transport_overrides. Any value that
differs from the preset shows as Custom; an empty field uses the preset.
A context rate caps the engine value rather than replacing it, and rate
is settable only for tools that have one. Edited through a Rates panel
on the engine builder, validated and clamped, carried through create,
update, duplicate, preview and launch.
Claude-Session: https://claude.ai/code/session_01Bb4u8p34euie1YFzb7qCPu
The option list takes the height the menu has instead of a fixed window.
Vulnerability grouping lists finding attributes first, then where they were
found.
Claude-Session: https://claude.ai/code/session_01VqBkGyjzd8GYYSSMaQHgMG
A Lookalike domains stage in the Analysis group generates permutations of the
target's registrable domain with dnstwist, resolves them with dnsx (a, aaaa, mx,
ns, one retry of silent names) and keeps the registered ones. A name whose only
answer is its TLD's wildcard address is dropped. Registered lookalikes are
fetched and compared with the target's page (ssdeep), and the highest ranked are
read over RDAP for registrar and creation date. Passive intensity skips the fetch.
Each lookalike carries one verdict: linked to target, similar page, MX
configured, parked, web content or registered. Linked is a redirect to the apex,
non-shared nameservers in common with it, or a project target. Placeholder MX
values and domain marketplace landers are not counted as mail or web content.
Rows live in lookalike_domains per scan; review state lives in lookalike_triage
per target and domain, so it carries across runs. The scan overview and the
target page show a lookalike line beside the associated domains, the target page
adds a cell, and a sheet lists the lookalikes with the changed characters marked,
per-verdict filters, records per row and review actions.
dnstwist and ppdeep join the tools package.
Remove the justification tells the Writing rules ban: a fact followed by a
semicolon, colon or 'so' clause in the test comments, the contrastive and
colloquial reads in the echo-guard, wildcard, api-spec and pacing docstrings,
and the colon-and-reason form in the coverage-strip and GraphQL notes. Wording
only.
A source-map or schema sweep sends many requests to one host, and a fragile or
rate-limited host answers a burst with timeouts and 429s that read as no result.
Requests to one host now pass through an adaptive governor: the per-request delay
doubles when the host returns a timeout, 429 or 503 and eases back as it answers
cleanly, with a cap on how many requests are in flight at once. A healthy host
runs at full speed.
A page that loads a script, frame or stylesheet from a domain that no longer
exists hands code execution to whoever registers that domain. A new support
stage reads the resources embedded in stored page bodies, groups them by
registrable domain, and resolves each apex against several public resolvers:
a domain returns NXDOMAIN at a quorum with no resolver disagreeing before it is
reported. A script or frame is high severity, a stylesheet medium, an image low.
No request reaches the target.
A service that publishes its own schema describes its whole API in one file, and
the crawler never reaches routes nothing links to. A new API-schema source probes
the common spec paths on every live web asset, parses OpenAPI v2/v3 and Swagger,
and emits one endpoint per path and method with its query and path parameters;
request-body fields are named in the endpoint detail and local schema references
are resolved. GraphQL endpoints are found by introspection. Endpoints from a
schema, and the schema documents themselves, carry an API badge.
An injection-echo template (command injection, RCE, SSTI, shellshock) proves
itself with a token echoed into the response body. A page that reflects the
requested URL into a canonical or og:url tag satisfies that matcher without a
command running. The guard drops such a match for those template classes only,
after URL-decoding both sides, and never for XSS or out-of-band checks. Each
drop is counted on the coverage row and shown in the coverage strip.
- no bot connected: one panel, BotFather link and a token field; Connect
checks the token with Telegram, stores the API key and starts the listener
- Settings sheet holds the bot, Change bot, Disconnect, the ceiling and the
rate limit; Disconnect removes the paired chats, pending requests and key
- a token for a different bot drops every pairing; deleting or replacing the
Telegram key under API keys follows the same rules
- page: bot header, pairing requests with the code and an up-to capability
picker, then Chats, Activity and Commands tabs
- activity records the command as typed, control characters stripped
A new AI Detection stage in the Analysis group runs julius v1.4.19 over
every web asset root and names model servers, AI gateways, AI
applications, cloud AI endpoints and MCP servers, with the models they
list. julius's MCP probe runs alone against /mcp and /api/mcp. Batches
of eight roots are paced to the scan's rate, AI ports are probed first,
and a minute budget and an asset cap are reported when they cut.
Results live on http_assets.ai_* and fold onto hosts and onto services
per ip:port, so an IP target with no host rows still carries them. Web
asset and service rows show the service, the host and service grammars
take ai: and ai.model:, the dashboard and target page count services per
product through /ports/ai, two built-in exposure rules flag AI services
and unauthenticated model lists, and compare watches ai_services.
The AI default ports join WEB_PORTS. julius ignores -f without a
positional target, so targets go on stdin.
The referenced-hostname harvest and the ownership judgement loaded every
stored body of a scan in one query; both now stream in batches of 200,
the ownership pass reading the bare-address responses first without
bodies. Referenced hostnames are inserted with ON CONFLICT DO NOTHING on
the scan and name, so a name another stage wrote in the meantime no
longer aborts the probe.
Finding titles become Third-party content on hostname and Dangling A
record, their descriptions become label and value lines, and impact and
remediation state one fact each. The internal address check, the
fixable flag, the tracking kind and the referenced hostname messages
drop their explanatory wording.
- Store each run's row, first-seen and retired counts (scan_deltas, scan_retired), valid while
the revision stamp matches scan_revisions; statement triggers on the seven result tables bump
it. Finalize warms, a beat backfill fills the rest, a live run answers from its last count and
queues a refresh. /scans 1.5 s -> 43 ms, dashboard overview 5-10 s -> 0.9 s.
- lead_cache holds a live scope for 20 s with a stale copy, and every build is single-flight;
search pages opt out.
- Leads: lowercase subdomain names (CHECK) and LIKE for host:, a guard for empty JSON arrays,
branches as MATERIALIZED CTEs read to the cap, every count in one statement.
- Addresses and services: the derived CTE as one grouped pass, rows and count in one execution
(page_rows), GIN lookups for page hosts, facets through grouping sets.
- Correlation carriers counted by EXISTS; takeover latest-sighting in SQL, no silent cap.
- Screenshot backfill partial index, nine unused low-cardinality indexes dropped, hostname
helpers memoised, vhost names normalised.
Launches past the limit stay pending and start in launch order when a slot
frees, so each running scan gets full speed and memory stays bounded.
- run_scan admits a pending scan under an advisory lock: running plus waiting
scans ahead of it must be under the limit
- finalize, cancel, pause, delete and a limit change start the next scans;
the reaper re-sends only scans whose turn has come
- automatic limit: live worker-scans stage slots over the widest step,
so --scale raises it; Settings sets a fixed limit from 1 to 50
- watch probes skip the queue, resume is never held
- queued scans show how many are ahead
The address grammar already answers it: a datastore port on an address
more than ten hostnames resolve to. On gov.np it names PostgreSQL on one
address behind 105 names. The query joins the Exposed services
suggestions.
Every web asset carries the analytics, tag manager and ads accounts its
page loads (Universal Analytics folded to its account, GA4, GTM, Google
Ads, AdSense, Meta pixel, Hotjar, Clarity, Yandex, HubSpot), read at
write and by the stored-response backfill. tracking is a correlation
kind, a host grammar field and the second cross link after the
certificate, judged by the page rule so a parked or default page never
vouches for its provider's account.
generic_page kept only ASCII, so a Devanagari or Chinese title read as
empty and every hub of a non-Latin estate was the server's. It keeps
Unicode word characters now.
A new disclosure check, internal_address, names the private addresses
and internal host names a response carries: any header, the pool member
a BIG-IP persistence cookie encodes, and links or quoted values in the
page. The body is read only where an address sits in a URL or a quoted
value, and every octet must be valid, so path data and version strings
never match. A migration clears hygiene_checked so the backfill
re-evaluates stored responses.
After the match, every inferred CVE row gets fixed_in: the nearest newer
release of the same product this scan saw that does not carry the CVE,
and how many assets run it. Each release's CVE set is read from the NVD
corpus, uncapped, so a scan at the match cap is still judged whole. A
version whose leading number sits far above the product's median is a
misread and never the fix. is:fixable filters on it, and the row and the
sheet name the release.
After the probe, every stored certificate, header, redirect and page is
read for names under the target that discovery did not return. They are
resolved, stored with where they were read (tls_cert, response_header,
redirect, page_link) and probed in the same stage, so every later stage
sees them. Two rounds, 5,000 names a round, only when discovery runs and
the run is not seed-only. URL and JSON escapes are decoded before reading,
and a name followed by another label is a lookalike, not a match.
A passive stage after origin_probe judges every owned name against the
redirect, the certificate, page links and the bare-address response.
rengine-foreign-site (high) when the name serves another organisation's
page, rengine-unhosted-name (medium) when the server answers with its
default and someone else's certificate. The estate drops every domain a
claim names, so a tenant no longer reads as a related domain.
provider_of moves to shared/definitions/estate.py for the worker; sign-in
and code hosts join VENDOR_DOMAINS.
http_probe becomes always-on support: saved engines, YAML and run overrides
drop its switch, passive still turns it off. Its every-port setting moves to
port_scan.http_on_every_port, the Passive Recon preset carries passive
intensity, and the launch solver includes the probe without letting it pull
in active support stages.
The summary bar draws one lane per event type over the last 30 days,
14 on a phone: a dot per day sized by its count, findings coloured by
the worst severity. Hover shows the day, a click or a drag picks the
days, the current period is tinted and Caught up is a dashed line.
Every event starts collapsed and expands in place. A rescan lists its
new critical and high findings, each opening its sheet. A bounty event
lists its rows with their actions. Hover is a background, never an
underline.
The period now picks whole runs, so a run's count is always what its
link opens. Days follow the viewer's time zone across the day filter,
the lanes and the headings. The badge counts events, a triaged finding
also hides its cluster replays, and a scan still running is dated by
its newest finding.
Claude-Session: https://claude.ai/code/session_015EWyHNJRpxkvhtapY3wNGx
With several workers a partial inspect reply looked complete, so a busy
worker-scans missing the window read as dead stages and got a second canvas.
- scan workers announce themselves in Redis (90 s TTL, consumer timer)
- the reaper skips its pass when an announced worker did not reply
- control slots default to 8: finalize p95 is 3.3 min and shares the queue
with step hand-overs
Stage tasks hold a slot for their whole tool run, so step hand-overs and
finalize queued behind them and background jobs took scan slots.
- worker-scans consumes scans (run_scan_stage only), scalable with --scale
- worker-control consumes scan_control: launch, hand-over, resume, finalize
- worker-default consumes default and critical
- reaper reads both scan queues; readiness requires a consumer on every queue
- connection budget counts every worker; max_connections 250
What's new lists only what changed: rescans that found new critical or
high findings, new programs, assets added to or leaving scope, bounty
table and rule changes, certificate hosts from watches, and targets a
scope or watch added. Events group by day on a rail, with a divider at
the Caught up mark. Each rescan names its worst new checks, and every
count opens the scan filtered to exactly those findings.
Program syncs now record payout, safe harbor and 2FA changes, and per
asset tier, bounty eligibility, severity cap and instruction changes.
The public feed sync diffs known programs for these too.
The search cache no longer stores a page that carries an error, so a
timed-out search no longer answers 0 rows for ten minutes.
Templates tab in the /scans idiom: All/Default/Custom count tabs, search,
audience and scope filters, rows with a cover thumbnail, section badges with
a +N hover list, format badges and a Generate action that opens the dialog
on that template. Default templates offer View instead of Edit. "Shipped" is
"Default" across report, wordlist, rule and typeface copy.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Only a capability stage's success covers a dimension; a support stage
covers through the rows it wrote. A cancelled run that finished takeover
and zone_transfer hid the previous run's findings on every page.
The targets list reads findings from each target's vulnerability
covering scan, and both strips gain a medium chip.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- sync the account's reports, bounties and standing from the HackerOne
hacker API into bounty_reports, bounty_awards and bounty_accounts;
money comes from /payments/earnings, totals stay per currency
- /bounty-hub/<platform> as a Bounty Hub child in the sidebar, in the
/scans idiom: stat strip, severity toggles, monthly chart with drag
range, stage tabs with filtered counts, URL filters, expandable rows,
keyboard navigation and a by-program view
- program names open the Bounty Hub program sheet, which links back to
the reports on that program and fetches unfetched scope on open
- hourly report sync gated on bug bounty mode and the sync interval
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- take a pairing code with GETDEL so two approvals cannot both claim it
- bound concurrent commands to DISPATCH_SLOTS and size the pool to fit
- keep the dispatcher across a token change, cancel follow-ups on stop
- approve and edit dialogs start from and send only what the ceiling allows
- activity list shows every row it counts; chats table in ScrollArea
- reload chats after a ceiling change; accept an apostrophe inside a word
- plain copy in bot replies, docstrings and the page; bot minutes read
from the pairing and step-up constants
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Confirmed now means the review state and nothing else: a second check
agreeing is corroborated, a replay on an equivalent web asset is
reproduced, and the WAF signal reads matched. KEV, ransomware and proven
are worded the same in the row, the sheet, the signals and the query
help. Signal labels and help drop the explanations, the empty states
drop the instructions, the coverage line says Not scanned like the
empty state below it, and the density option matches /scans.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AQDJwqjTD69CUKa95DjM8H
The sheet opens on the decision: severity, KEV and the facts line in the
header, review state and rescan in a fixed action bar, then evidence,
exploitation rank, EPSS and reach as four cells that each filter the
table. Content moves into tabs: Overview (description, remediation and
the web asset with its neighbour counts and other findings), Evidence,
Same check, Intel and Notes. The single-finding endpoint now returns the
same asset correlation as the search.
Signal and intel copy states facts only, and the overdue signal names
the deadline date instead of a day count that went stale.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AQDJwqjTD69CUKa95DjM8H
The findings table follows the scan history idiom: a summary strip that
collapses to one line, review-state tabs, filter pills, and rows that
expand into a brief with the web asset, the other findings on it, the
same check elsewhere, the evidence and the intel.
Each finding now carries the open findings on its host by severity, and
its asset carries open ports, names on the address, endpoints and
inferred CVEs, one grouped query per page. Columns hide by the table's
container width, so the table fits at any size without scrolling
sideways.
The exploitation ring in the row becomes a rank number, the left row
bands are gone, and the strip drops the Info chip.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AQDJwqjTD69CUKa95DjM8H
Each row carries open critical, high and medium counts, computed with the
Vulnerabilities page's suppression rules, and whether the vulnerability
stage ran. Medium is behind a toggle in the run brief; low and info are
not shown.
- Row brief with Findings (inline triage), Changes, Coverage and Engine tabs
- Latest per target view with earlier runs nested beneath
- Query grammar: target, engine, status, severity, is:partial, is:added
- 30-day runs chart with a date brush, strip counts equal to the rows opened
- Findings trend per target, inline compare sheet, keyboard navigation
- API: findings and target_runs on ScanRead, severity/short/added/date/latest
list filters, GET /scans/trends and /scans/daily
- Removed the unused /scans/targets grouping and the old list components
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EuoZt3GW4VjRSF7FJeFtuV
Two disqualifiers on the candidate, measured against the 441 stored rows.
A fronted name is compared against every address it resolves to, not the
one httpx recorded, so an address the name already points at is no longer
a bypass. And a certificate no longer mints a candidate on its own: it is
presented by load balancers, mail hosts and admin panels that serve none
of the site, and stood alone in 125 rows of which one reproduced.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GyVHGU2ARYyws9SHWQuCdo
A nested chord canvas put the whole remaining pipeline into every
stage message, duplicated per sibling per level: 33 MB per message,
39 of them filled a 1 GB Redis, and the worker was OOM-killed
deserializing them. Every later launch then failed with "command not
allowed when used memory > 'maxmemory'".
shared/services/orchestrator/canvas.py now builds one step at a time;
a step's chord body is app.tasks.scan.run_scan_step(steps, index+1),
which dispatches the next step once the current one settles. Messages
are ~1 KB now; tests/pipeline/test_canvas_shape.py caps them.
Also, from an end-to-end review of the pipeline:
- The reaper re-queues a PENDING scan with no launch anywhere in the
queue, so a lost or failed send_task no longer strands it forever.
api create() now dispatches off the event loop and records a failed
dispatch the way create_batch already did.
- Cancel no longer revokes queued stage tasks. A cancelled run used to
walk every remaining step recording one aborted row per stage, and
whether finalize ran depended on a race with the revoke broadcast.
The step body now finalizes a cancelled run directly, and the api
also dispatches finalize so a run with no live canvas still settles.
- The reaper only skips scans that actually have a message queued,
read off the scans queue, instead of skipping every scan whenever
the queue holds anything.
- Every beat tick carries expires, so a worker that comes back after
being down does not replay hours of stacked ticks.
- worker-default and worker-beat restart on their own.
- _run_process kills the tool in a finally; a soft time limit raised
inside proc.wait used to leave the tool running past the stage.
- on_worker_ready crashed on a missing Consumer attribute, so the
worker's IP-range and threat-feed warm-up never ran.
- Worker logging now goes through the root logger; it was configured
on a "rengine.worker" logger nothing writes to, so INFO logs (task
starts, step hand-overs) never appeared in the container output.
- _normalize_url no longer raises on an unparseable value, which had
been killing the whole bounty feed sync on one bad scope entry.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0181JfVNatLVWnDbtGWRWPov
Out-of-band testing is an instance setting — `off`, `self_hosted` or
`public` — with the server, the token (the INTERACTSH API key row) and the
wait resolved once in `shared/services/oast.py` and read on the stage
thread. A mode that cannot be used is off and says why: self-hosted with no
server, or public without the disclosure acknowledged. A fresh instance is
off. `POST /oast/test` asks a public address for `/poll` over HTTPS and
reports that the server answered, never that OAST works.
The engine keeps one switch. `FieldNeeds` puts the resource key on the
field and `GET /engines/catalog` fills `unavailable_reason`, so the builder
renders the instance reason beside a switch that stays settable.
Measured on nuclei v3.11.1: with `-no-interactsh` a check that needs a
callback still loads and the request is refused — 0 requests, exit 0. So
the batch reported COMPLETED and every origin in it read `scanned` for
checks that never touched the target. The selection now drops them when
nothing is listening, one coverage row reports the count and the reason,
and `Tier.OAST` is left out of the planned tiers.
Membership is what a check references, not what it is tagged: 615 of the
12,586 official rows reference an interactsh placeholder and 233 of those
carry no `oast` tag. `vuln_templates.needs_oast` is parsed at index time; a
library resync fills the column.
The wait is paid on out-of-band batches alone, which sweep for 30 minutes
so it is paid fewer times, and the tier names the server on its own rows.
A finding's callback is rendered on the sheet from one label map; the list
path is bared to the metadata.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A target can follow the check library: once a day, when the library gains
checks, one focused run tests every web asset the target's completed scans
found, with those checks only. Per-target watermark, one sweep lock, a
`daily.run` chain that registers future once-a-day jobs, two notifications,
a What's new group, a "New" badge on the library, a monitored-target signal
and a "New checks runs" scan filter.
The pipeline review behind it, in three passes:
Planner and scanner. The deep sweep is its own tier, outside ROOT_TIERS and
after replay, so a cut costs the tail and cluster members are still
confirmed; the one-request tier runs first, sized on its clustered cost;
blind core ranks CISA then VulnCheck then severity then cost; the planned
tiers come from the selection; skipped hosts are marked alone; library gaps
are one row per run; the sweep cap is 8 requests.
Output. Payload-free fingerprints for fuzzing, out-of-band and dalfox
findings; suppression follows the replay edge; the digest baseline is every
earlier scan; host and port read through split_host_port; interaction obeys
store_evidence and redaction; the rollup excludes replays.
Tools. nuclei's exit code, abort and budget are read; a tool arg cannot
restate a stage flag under its short spelling; one refused row costs one
row; a headless block decides the protocol.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B58eEM68a5qDFTQXPE5aoW
Three corpora, one miner: web asset roots, the responses endpoint_probe
read, and a scanner's proof. `endpoint_responses` is its own table so the
nine select(Endpoint) sites do not carry every body; retention deletes
those rows with the web asset bodies, and protects each target's newest
census run.
A passive leaf past the last awaited level joins the deferred tail, so
the finding corpus lands before finalize re-mines it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B58eEM68a5qDFTQXPE5aoW
GET /whats-new/visual pairs every host two consecutive settled census
runs both captured and keeps the ones whose screenshot dHash moved past
the cluster distance, so the pair is no longer the same page. Each pair
carries the hamming distance and the compare fields that moved; a pair
where none moved is a silent redeploy, its own badge and filter. The tab
shares the page's period, target and text filters, shows before and
after with hover previews, and opens the web asset sheet, Compare runs
or a scan from the card. The feed carries the pair count for the tab.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XqZAsFckWFdr7EFiyZH9V
GET /whats-new reports what appeared in a project since the operator
last caught up, grouped by the run or program it came from. Ten kinds,
each noise-gated: web assets, services, findings and secrets from repeat
scans (a first scan sets a baseline and reports nothing, certificate
names stay with the watch), in-scope assets, programs, certificate hosts
and targets from Bounty Hub, and a Gone pair of out-of-scope assets and
retired web assets. A run group's "All N" opens that scan's tab with
is:new, so the count equals the rows. POST /whats-new/seen writes the
mark from the Caught up button alone; GET /whats-new/unseen feeds the
sidebar badge. Scope events count for programs the project watches or
whose scope covers a target; program events count library-wide, with a
ring switch for the whole library. The 13-week grid is per kind and
doubles as the period filter, by day or range.
The page carries kind tiles with facts and sparklines, a source switch
(All, My targets, Bounty Hub) that collapses in corporate mode, target
and program pickers, a text filter, rows that open the results tables'
detail sheets in place, inline Add target, Watch, Scan, Mute and Remove
target, card collapse with card-level Add N targets, shift-click range
selection, keyboard navigation, copy on hover, a sticky filter bar and
a reload when a run finishes.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XqZAsFckWFdr7EFiyZH9V
The feed reports new surface only: web assets a later scan was first to
find, scope a program opened, programs added or made public, hosts a
certificate revealed under a watch, and targets Bounty Hub brought in by
watch or scope. A first scan yields nothing, hand-added targets are the
activity log's business, and scope removals stay in Bounty Hub. Program
events count for programs the project watches or whose scope covers one
of its targets. The response carries per-day buckets per kind, drawn as
a week-column grid above the list; a lit day scrolls the list to its
header. Days are keyed in UTC on both sides. Windows are 7, 30 and 90
days, default 30.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfLmNNiLk5VdePfq9CpugW