Repository navigation
Releases
Releases · rdlugs/sentryward
Sentryward 0.4.0
Sentryward remains pre-1.0, and this release changes some default behaviour:
adding someone to a team now sends them an invitation to accept, and
invite-only registration accepts fewer team invites. AI findings still need
human verification.
Upgrade notes
- Back up the database and
ENCRYPTION_KEYbefore upgrading. The server
applies this release's migrations on start. - Adding a team member by email now creates an invitation the recipient must
accept, and the add-member account picker is hidden. To restore immediate
additions, an instance admin turns on Allow direct member additions
under Administration > Instance settings > Policy. It is off on upgraded
installations too; existing memberships are unaffected.
GET /api/workspaces/:id/member-candidatesanswers 403 while it is off, and
POST /api/workspaces/:id/membersreturns{ invite, link, emailed }
instead of{ member }. - In invite-only mode, a team invite only creates new accounts when its
creator is currently an instance admin. Reissue other outstanding invites
from an admin, or use an instance invite. WORKER_SHUTDOWN_DRAIN_MS(default 10 minutes) sets how long a stopping
worker waits for in-flight reviews before handing them back to the queue.
KeepWORKER_STOP_GRACE_PERIODa few minutes above it (the defaults, 10m
and 15m, already are), or Compose kills the worker before the hand-back.
Added
- The LLM Config tab shows what was detected for the model that will run the
review (thinking and tool support), and a Test model button runs it on that
connection without saving. Connections whose key was rejected, could not be
verified, or are blocked are marked in the connection picker (#190). - Fallback models can be moved up and down (#191).
- The LLM Config tab shows the model's price, with a link to set one when it is
unknown, and what anautocontext budget resolves to for the model, max
output tokens and thinking level, warning when a typed budget leaves no room
for the reply.GET /api/config/schemanow also returns the context windows
and thinking budgets, andGET /api/settings/model-prices/lookup?model=
returns the price a review of any model gets (#192). - The Triggers tab can check a sample change, under a Will this be reviewed?
toggle that starts closed: an event, title, author, labels,
branches and draft state are run through the worker's own skip rules with
the unsaved settings, and it answers Reviewed (with or without the PR
summary, every file or new commits only) or the reason it would be skipped.
It is served by the newPOST /api/config/triggers/check. - The header links to the GitHub repository beside the theme picker.
Changed
- The verification model and verification thinking are greyed out while
Verify findings is off, and the walkthrough model while the walkthrough is
off, with a link to the tab that turns them on. Saved values are kept (#193). - LLM Config tab polish: effective values are only shown when they come from
somewhere other than the scope being edited, Profile sits above the tabs
since it applies to all of them, Connection capabilities overrides are under
an Advanced disclosure (kept open while one is set), Reset to defaults
explains what it keeps and why it may be disabled, and the Model,
Temperature, Max output tokens and Context budget descriptions match what
the server does (#194). - The Finding Types tab is regrouped into Severity, What gets reported,
Accuracy, Scope and context, Static analysis and Fixes, with a summary panel
beside it showing what a review reports and blocks on, unsaved edits
included. A severity scale labels each severity as blocking, reported or
hidden, and warns when the block level sits under the report level (those
findings are dropped before they can block). Caller depth is greyed out
while Surrounding code is off, and the agent limits unless Review context is
Agentic. Categories have descriptions, a count and All/None buttons, and Min
confidence has a slider. On every tab, a field's effective value now sits
beside its label and the "leave empty" hint moved behind an info icon. - The Triggers tab is regrouped into Automatic reviews, PR summary, Commands
and replies, and Skip rules (by change, people and branches), with a summary
panel of when Sentryward runs, unsaved edits included. While automatic
reviews are Manual only, the push switches, the PR summary trigger and the
skip rules are greyed out, since the worker never reaches them; the PR
summary trigger is also greyed out while the walkthrough is off, with a link
to the Display tab. Each skip list is tagged with how it matches, the review
trigger spells out what happens to drafts, and a repository's list says it
replaces the inherited one. - Team membership requires consent. Adding someone by email creates an
email-bound invitation (emailed when SMTP is configured, otherwise a link to
copy), and access begins only when they accept. Team admins no longer see
which emails have accounts, unless an instance admin turns on Allow direct
member additions, which brings back the account picker and immediate
additions (#206). - Stopping or upgrading the worker no longer leaves unfinished reviews waiting
for stale-lock recovery. The worker waits up toWORKER_SHUTDOWN_DRAIN_MS
for in-flight reviews, then puts any still running back in the queue without
using one of their attempts, so the new worker picks them up about half a
minute later. Each handed-back review stays leased until any forge call the
old worker had in flight has finished, so it cannot be run twice (#212).
Fixed
- Invite-only registration now requires an instance invite or a team invite
whose original creator is currently an instance admin. Other team invites
still let existing accounts join, but cannot create new accounts. Demoting
or deleting the creator removes a team invite's registration authority;
resending does not grant it (#205). - Logging in with an unknown email runs the same argon2 work as a wrong
password, so response time no longer reveals whether an email has an
account. Registration still answers 409 for a taken email, by design (#208). - Audit log export filenames use the viewer's local date instead of UTC (#214).
- When two registrations race for the same email, the one that loses gets the
usual409email field error instead of a server error. - The SSRF guard refuses reserved IPv4 ranges and IPv6 addresses that carry
an internal IPv4 address (IPv4-mapped, NAT64 and 6to4), classified by the
address they carry. Local-only mode keeps its previous list of internal
addresses, so it does not start trusting public hosts reached through those
ranges (#207). - The agentic
search_codetool on GitHub sends the model's term as one
quoted phrase, so search qualifiers in it (such asrepo:ororg:) can no
longer point the search at other repositories. An empty term finds nothing
instead of failing the caller search (#211). - Turning reviews on for a repository twice at once, or after a forge request
that timed out, no longer leaves a duplicate or orphaned webhook. The
registration is reconciled with the forge's existing hooks by URL, and a
hook created for a repository removed meanwhile is deleted. timeAgorounds values that reach a full unit up to that unit instead of
showing "60 minutes ago" or "24 hours ago", and past and future times now
round halfway values the same way (90 seconds ago reads "2 minutes ago")
(#213).- The worker's forge cache can no longer grow without bound on large changes.
It was limited to 200 entries whatever their size, so a few big monorepo
pull or merge requests (whole file lists with patches, or full files) could
push the worker toward running out of memory. Cached entries now also stay
under 64 MB in total, and a single answer over 8 MB is used for the review
but not cached. Closes #188. - Renewing a GitLab OAuth connection's access can no longer lose the tokens
GitLab issued. If the refresh transaction timed out or failed to commit after
GitLab answered, the new tokens were rolled back while the old refresh token
was already spent, and the connection needed a manual reconnect. The token
request is now capped to the time the transaction has left, and tokens from a
refresh whose transaction fails are still stored, unless the connection was
updated in the meantime (#187). - A review whose worker stalled past the lock timeout, and was retried by
another worker, can no longer finish or fail the retry when it wakes up.
Finishing and failing a job now apply only to the attempt that holds it, so
the retry's result and findings are stored instead of being dropped.
Closes #185. - A pull or merge request that is closed and then reopened without a new push
is reviewed again. The review cancelled by the close no longer counts as one
already done for that head, so the reopen webhook queues it instead of
answeringduplicate. Closes #184. - A forge that accepts the connection but never answers no longer hangs a
review or a webhook. Every forge API request, including each redirect hop,
is now aborted after 30 seconds, so the worker fails the run and frees it
instead of keeping the whole change in memory, and webhook handlers that
call the forge respond. Closes #186. - Two reviews of the same pull or merge request no longer run at the same
time. A newer push now waits for the running review to finish, so an older
head that finishes last can no longer overwrite the newer review's summary
comment, reopen its fixed findings or become the latest review. A review
cancelled from the dashboard or stopped by the run time limit finishes
stopping before the next one starts, and if a worker dies mid-review the next
review of that change starts within about three heartbeats (3 minutes by
default). Close...
Sentryward 0.3.0
Sentryward remains pre-1.0, and this release changes some default behaviour:
review context now follows callers two hops from the changed code, and
changing your password signs out your other sessions. AI findings still need
human verification.
Upgrade notes
- Back up the database and
ENCRYPTION_KEYbefore upgrading. The server
applies this release's migrations on start. review.impact_depthdefaults to 2, so reviews search for callers of
callers (within a cap of 8 code searches per review). Set
review.impact_depth: 1to keep the previous context.REVIEW_RETENTION_DAYSis optional and unset keeps everything, as before.
Once a retention period is set (here or under Instance settings), the worker
deletes and clears older review data hourly, including the findings of
changes with no activity in that period.GET /api/usageno longer returnsrecent; API clients that read it should
useGET /api/usage/recent.
Added
list_dirtool for agentic review passes (#144). With
review.context: agentic, a pass can now list a directory at the change's
head revision on GitHub and GitLab, e.g. to find a changed file's tests or
siblings. Paths followread_file's rules (relative, no..), directories
end in/and symlinks in@, a listing shows at most 50 entries, and its
output is marked as untrusted and counted against the pass's tool budget.
A directory larger than the forge returns in one request (100 entries on
GitLab, 1000 on GitHub) is reported as incomplete rather than given a wrong
count. Listings are cached by head sha like file reads.- Phone layout for the Reviews and Findings lists (#165). Below 640px wide
(phones, or high browser zoom) each review and finding is a stacked card
instead of a table row, so the lists reflow at 320px without scrolling
sideways. Cards keep every column's value, the row menu and click-anywhere
to open; a review's earlier runs expand inside its card, and a finding's
change title is written out rather than left in a tooltip. Findings get a
"Sort by" picker and a direction toggle in place of the sortable headers.
Long active-filter chips truncate instead of widening the page, and the
pagination's "Rows per page" label reads "Rows" on phones so its controls
wrap. - Command palette and a global "Review a PR" action (#161).
Ctrl+K(Cmd+K
on macOS, where plainCtrl+Kstays the text fields' "delete to end of
line") opens and closes a palette from any dashboard page, also from the top
bar's Search button: it jumps to any page (admin pages only for admins, plus
Settings), lists repositories and opens a repository's reviews, starts a
manual review, and queues a review for a pasted pull or merge request link.
It does not open over another dialog, sheet or menu, says when repositories
are still loading or could not be loaded, and closing it while a link is
being queued leaves you on the page with a "Review queued" toast to open it.
A "Review a PR" button in the top bar opens the manual review sheet from
every page; Home and Reviews open that same sheet. - Keyboard navigation and severity jump for findings on the review page
(#157).Ctrl+J/Ctrl+Umove between findings in display order,Ctrl+O
orEnterexpands or collapses the focused one,Ctrl+Ccopies its agent
prompt (or the selected text, when there is a selection), andCtrl+?opens
a dialog listing the shortcuts. Shortcuts are ignored while typing in a
field and inside menus and dialogs, andCmdis left to the browser. A sticky
"Jump to" bar under the header shows per-severity counts of the visible
findings and focuses the first finding of a severity; it also opens the
shortcuts dialog. Focus from the keyboard, a jump, Tab or a click shares one
position, and the focus ring stays visible after a mouse-started jump.
Ctrl+OandCtrl+Conly act while a finding has focus, a shortcut with
nothing to act on is left to the browser (so its ownCtrl+JandCtrl+O
still work, whileCtrl+Unever opens view source on the review page),
Ctrl+?follows the active keyboard layout, and a jump clears the bar even
when it wraps on narrow screens. - Key metrics on Home (#154). Once the workspace has a review, Home shows
four tiles above the setup checklist: open critical/major findings, reviews
and failed runs in the last 7 days, and estimated spend over the last 30
days. Each links to the matching list (open findings sorted worst first,
runs, failed runs, Usage on the 30-day window), loads with its own skeleton,
and shows "-" if its stats fail to load.Stattakes alinkLabelso a
linked tile can name the page it opens. - Progress for running reviews (#156). The worker records the current stage
on the job (newprogresscolumn onreview_jobs): fetching the change,
preparing, reviewing (withn of mpasses, and planning, context and
walkthrough as details), checking findings and publishing, listing only the
steps the run's config turns on.GET /api/reviews/:idreturns it, and the
review page shows a step list with elapsed time instead of a single spinner
line (queued reviews show how long they have waited, and a job requeued
after its worker stopped says it is waiting to retry). Until the run's
config is known only the current step is listed, so the list only grows.
Writes are best effort, coalesced, and only made by the attempt that holds
the job; a new claim clears them, and every run waits for its last write
before returning. - Privacy controls (#109). A new "Privacy and data" section of the README
lists what Sentryward stores, what is sent to model providers and forges,
and how long it is kept. Instance admins get an Instance settings page
(GET/PUT /api/admin/settings, newinstance_settingstable) and
workspace admins a Privacy page (GET/PUT /api/privacy, new
workspace_privacytable) to restrict model requests to chosen providers
and hosts (exact names,*.example.comwildcards, optional ports) or to
local endpoints only (hosts that resolve to private or loopback addresses).
A workspace can only narrow the instance policy. The policy is checked
before a review posts anything, for every model it may call, including
ones named in.sentryward.yaml: a refused review, walkthrough or verify
model fails the review with aBlocked by privacy policy:reason, a
refused fallback is skipped with a warning, thread answers post a short
notice instead, and LLM connection save, test and model-list requests to
a refused host are rejected before they are sent (the connection shows as
"Blocked by policy"). Retention is now configurable from the dashboard
(instance period, falling back toREVIEW_RETENTION_DAYS, which workspaces
can shorten) and also purges the findings, posted-comment records and
finished thread replies of changes with no activity for longer than the
period. Every outbound model request (reviews, walkthroughs, verification,
capability probes, thread answers and connection tests) is recorded in a
newllm_request_logsaudit table with its workspace, repository,
purpose, provider, host, model, token counts, latency and outcome, never
prompts, code or error text. Admins can browse, filter and export it as
CSV or JSON on the Privacy page (/api/privacy/audit-log) and, for every
workspace, in the Audit log tab of Instance settings
(/api/admin/audit-log), and an instance audit retention period removes
old entries. - Cross-file impact analysis (#108). Review context now follows callers of
changed code more than one hop (review.impact_depth, 1-3, default 2, 1 in
thefastprofile; also in the dashboard under Findings > Review context):
each caller's enclosing function becomes the next name searched, within a
shared cap of 8 code searches per review (generic method names such as
runorhandleare not traced), and further hops are the first context
dropped when a pass is out of room. Callers of declarations the diff
changes or removes, including parameters on later lines of a multi-line
signature, are searched first and labelledsignature changed/
removed, and the review prompt asks for broken contracts to be reported
withevidence: contract, the affected callers inimpactand their files
inevidenceFiles, which the summary and inline comments show as
Affected for contract findings. The walkthrough lists risky areas
(authorization checks, database writes and schema changes, API routes, and
route handlers or auth modules reached through callers; test files never
count) in the summary comment and the dashboard's walkthrough card, both
hidden byoutput.walkthrough_blast_radius, and they raise its blast
radius: at leastmediumfor any risky area,largeonly for a removed
auth check. Reviews stored beforereview.impact_depthexisted still allow
an incremental review on the next push. - Model and review-cost benchmarking (#107). Workspace admins can set their
own model prices under LLM Connections > Model prices (GET/PUT /api/settings/model-prices,DELETE /api/settings/model-prices/:id,
newmodel_pricestable); a review's calls are priced at them when it
runs, ahead of the built-in table. A model nothing prices now records no
cost and is flaggedunpriced, so its cost reads as unknown instead of $0
on the Usage page (totals, table and recent reviews), the Reviews stats
strip and the review sidebar; the digest'susageTotals, review stats
tokens and recent usage rows carryunpricedCallsfor it. Ollama models
stay free unless priced. A workspace price keyed by any prefix-stripped
form of a router id (cx/gpt-5.6-solforopenai/cx/gpt-5.6-sol) applies,
and the price list's models carry thepricereviews get now, so the
"no price" warning clears for them. A saved price's model id can't be
changed...