Web UI
The Web UI is served by swarmotterd from the same address as the API.
http://127.0.0.1:9091/
Change the listener with:
[api]
bind_address = "0.0.0.0:9091"
require_auth = true
auth_token = "replace-with-a-long-random-token"
Authenticated access is strongly recommended when binding outside localhost.
With require_auth = true, the Web UI asks for the token once and keeps it in
browser-local storage. A trusted-LAN deployment may set require_auth = false;
the UI then uses the same-origin API without a token prompt. Every client that
can reach an unauthenticated listener can control SwarmOtter. Browser requests
must remain same-origin, and reverse proxies must preserve the public Host.
Add torrents
The Web UI supports:
- Magnet link entry.
- File picker upload for
.torrentfiles. - Drag-and-drop upload for
.torrentfiles anywhere in the app window. - Metadata-preview checkboxes for magnet and
.torrentintake.
Dropped .torrent files are sent to:
POST /api/v1/torrents/file
The app refreshes the torrent list after successful upload.
Selecting Metadata preview adds a .torrent in a paused state, or lets a
magnet fetch and verify only its metadata through the contained daemon path.
Once a magnet preview has its file list, Torrent Details shows the captured
intake policy and the payload gate. Choose file priorities as needed, then use
Start or Resume to allow normal payload transfer. A preview never turns
into a payload download merely because a profile or queue setting changes.
Torrent list
The Peers column shows active peer workers / known peers from the torrent summary response. The main UI area uses the available browser width so wide tables can show operational details without being capped to a narrow centered column. Per-row torrent actions are icon buttons with accessible labels. The Details action opens keyboard-accessible lifecycle, queue, move, label, bandwidth-limit, file-rename, and tracker-edit controls. Removing one torrent offers separate Cancel, keep-data, and delete-data choices.
Torrent Details displays an explicit identity row. Hybrid torrents show both their v1 SHA-1 and v2 SHA-256 identifiers; this avoids presenting the v2 value as if it were a v1 registry hash. Older daemon responses retain the legacy v1-hash fallback during an upgrade.
The torrent list is an interactive table. Click a column header to sort by
that column, and click it again to reverse the direction. Header filters can
filter individual columns: status and health use list filters, while numeric
columns such as size, progress, rates, ratio, and peers accept comparisons
such as > 0, >= 50, < 10, or = 1. The toolbar search remains a global
filter across common torrent summary fields, and Clear Filters resets both the
toolbar search and column filters.
Torrent rows can be selected with checkboxes. The torrent toolbar can select
all currently visible rows, clear the current selection, and remove all selected
torrents. Bulk removal removes torrent records through POST /api/v1/torrents/remove and keeps downloaded data.
Tracker details
Torrent Details → Trackers keeps announce status separate from scrape status.
The table shows the last scrape time, retained seeders/leechers/downloads in
S / L / D order, and the compatibility counts used elsewhere. A scrape error
is displayed beside error while the last successful counts remain visible;
unsupported means the tracker is UDP or its final path is not derivable from
announce*. UDP announce remains supported—only UDP scrape is unsupported.
All tracker URL, status, time, count, and error values are escaped before being inserted into the table. Scrape is operational telemetry scheduled by download, magnet, reannounce, completion, and active seeder tracker activity; it is not a separate user mutation.
The Details summary also displays Last error from the native torrent
summary. If every attempted configured tracker fails and no usable alternative
source exists, the state becomes tracker error and this row retains the last
tracker failure. Reannounce, Resume, or Start Now clears the terminal error and
starts a new attempt.
Per-torrent seeding policy
Torrent Details includes a Seeding Policy card. Its read-only summary reports
the uploaded-byte count, ratio, exact seeding status, stored ratio/idle targets,
effective ratio/idle targets after global inheritance, and whether seed-forever
is enabled. Status values are displayed as not eligible, queued, active,
stopped ratio, stopped idle, or stopped manual.
Use the Ratio target and Idle target controls as follows:
- Select Inherit global ratio or Inherit global idle to store
nulland use the corresponding value from Settings > Seeding. - Clear inheritance and enter
0to request an immediate automatic stop. Zero is a real target; it is not the same as inheritance. - Select Seed forever to suppress both effective automatic targets while preserving the stored overrides for later use.
Save Seeding Policy replaces all three per-torrent fields together. The UI waits for the server response and reloads Torrent Details before displaying the new summary; it does not predict a status transition locally. Invalid input or a persistence failure is shown in the card’s alert and leaves the last rendered stored/effective values unchanged. A policy edit never resumes a torrent that an operator manually paused; use Resume or Start Now when that is intentional.
Large-library operations console
For large libraries, the Operations Console is optimized for speed and low layout churn. The list is designed for high-count visibility with:
- server-side search plus state, health, and performance-condition filters,
- table sorting that round-trips through the server query endpoint,
- a browser-local saved view for search/filter/page-size/sort state,
- count-oriented list requests and pagination for incremental refresh,
- clear confirmation paths for bulk destructive operations, and
- detail views that avoid forcing a full table reload.
The underlying /api/v1/torrents/query endpoint also supports label, storage
root, peer/rate threshold, counts-only, and optional grouping parameters for
external automation and future UI views.
Protocol encryption controls
SwarmOtter can negotiate MSE/PE peer encryption. The Settings screen exposes
torrent.encryption_mode with these choices:
disabled(plaintext handshakes only),preferred(contained TCP/uTP attempts use MSE/PE first, with a plaintext fallback only on the same selected transport),required(refuse plaintext with no fallback).
The default is preferred. The UI keeps this control in the same Settings edit
flow as other daemon config because it changes peer-wire compatibility behavior.
Policy profiles
Settings includes a Policy profiles editor for the persisted profiles
configuration section. The Add screen can choose a profile and labels before
registration. Torrent Details shows every effective profile value with its
source and can set or clear an explicit profile assignment. Profiles may set
an optional encryption_mode; Torrent Details shows its effective source and
can set an explicit per-torrent encryption mode or choose Inherit profile or
global mode to send an explicit null clear. Storage paths and the initial
start-or-paused decision are shown as create-time snapshots: profile
reassignment does not move existing data or revoke a queued torrent’s
admission. Queue priority, seeding, bandwidth, and peer encryption remain
explainable live inheritance.
Profiles can also define ordered tracker-host enablement/priority plus create-time intake exclusions, complete/incomplete content organization, single-file top-level folders, and active-only partial suffixes. Torrent Details displays the effective live tracker policy, stored structured exclusion rules, organization values, resolved complete/incomplete path preview, explicitly unwanted file indices, and whether a metadata-preview gate remains active. Those intake choices are fixed at registration; later profile edits do not silently alter an existing torrent, while tracker-host policy remains live.
Peer admission
Settings includes a Peer admission panel for the global local-rule,
blocklist-path, manual-ban, and peer-ID-prefix policy. It reads the live policy
from GET /api/v1/peer-filter, showing the effective direct rules, local-source
load/skipped-line results, manual bans, rejection counters, and any fail-closed
detail. The editable fields remain part of the full Settings configuration
snapshot, so reloading or saving Settings preserves the complete peer-admission
configuration rather than overwriting it with status data.
The torrent Details Peers tab can ban an IP. These are global manual bans,
not torrent-local exceptions, and the Settings panel lists them with a global
Unban action through POST /api/v1/peer-filter/unban. A peer row is marked
banned only when its IP is in that explicit manual-ban list; merely viewing
the table does not perform a new admission decision or change rejection
counters. Peer admission rejects unwanted candidates but does not replace the
required contained network path.
Storage root diagnostics
The Doctor view surfaces storage diagnostics from GET /api/v1/storage/roots
so operators can:
- review per-root free/available bytes before large add bursts,
- identify which roots are close to configured reserve thresholds, and
- diagnose storage pressure alongside active write/recheck activity and configured root controls, mount options, CoW strategy support, and observed sustained payload-write/verification throughput.
Storage reserve fields in configuration are [storage].minimum_free_space_bytes
and [storage].minimum_free_space_percent. When configured, add operations are
rejected before writing data when the target root cannot satisfy the configured
reserve.
The Storage settings panel also manages repeatable [[storage.root_controls]]
entries. Each row exposes a lexical path plus active-download, active-byte,
write-rate, and concurrent-recheck limits. The Doctor table shows the matching
control root, declared active bytes, active rechecks, and saturation warnings
so an operator can distinguish a local root budget from global queue limits.
It also exposes durable placement for fast-resume metadata, daemon-state
defaults, and fallback temporary payload storage, plus the explicit CoW
strategy. A state-directory change is shown as restart-required; a resume
directory change is rejected while unfinished data remains. The UI does not
offer an implicit filesystem optimization: NOCOW is explicit, applies only to
new supported Btrfs files, leaves existing files unchanged, and errors rather
than falling back silently when an existing writable file lacks the flag.
Performance diagnostics and autopilot visibility
The torrent detail view uses /api/v1/torrents/:hash/stats as its primary
diagnostic source. Existing health sub-scores and reasons are the basis for the
autopilot-oriented “why is this slow?” explanation and are updated from the same
contained network observations as engine and network health reporting. In
act mode, the daemon may apply bounded actions from those observations; the
details page shows the current decision and rationale.
In autopilot visibility mode, the UI reads:
GET /api/v1/autopilot/statusfor the global autopilot mode.GET /api/v1/network/healthandGET /api/v1/network/diagnosticsfor any containment condition that may block or bias tuning decisions.GET /api/v1/torrents/:hash/statsfor peer-level health and scheduler signals.GET /api/v1/torrents/:hash/autopilotandPOST /api/v1/torrents/:hash/autopilotfor per-torrent decision views and mode override controls.
The Settings tab includes an Autopilot card for the global
disabled / observe / act mode. The default is act, and Torrent Details
keeps the per-torrent override control.
The Settings screen uses a two-panel layout: section navigation on the left and the selected settings group on the right. Save, reload, and reset controls sit in the Settings header. Saving submits the full configuration snapshot. If an operator intentionally makes the config path read-only, a failed persistence attempt falls back only to the live-safe bandwidth, queue, seeding, and autopilot PATCH; the UI reports that other changes were not applied.
The details page renders a compact “why is this slow?” report with these fields:
- active/global/autopilot mode state.
- machine-readable reason identifiers and recommendations or applied-action candidates.
- no-progress queue-slot release recommendations when a stalled active torrent is eligible to let queued work proceed.
- snapshot signals and network-conditions impact for operational context.
The UI should present autopilot recommendations as human-readable entries with underlying machine-readable identifiers (for operators and automation clients) and continue to honor the fail-closed containment model.
Notifications
Transient operation feedback is shown as toast notifications instead of inline status text. This includes torrent add/upload results, user-initiated torrent removal, external removals observed while the complete unfiltered library is visible, bandwidth setting saves, and watch-folder scan results. Filtered or paginated result changes are never treated as proof that a torrent was removed.
Toasts display for 5 seconds by default. The display time is a browser-local UI preference that can be changed in Settings > Notifications.
Network health
The UI shows network containment health from:
GET /api/v1/network/health
Detailed network checks and path diagnostics use:
GET /api/v1/network/diagnostics
SOCKS5 TCP proxy
Settings > Network exposes an opt-in SOCKS5 TCP CONNECT card with proxy host,
port, and optional username/password fields. Enabling it clears the uTP and DHT
controls because this release deliberately blocks UDP tracker, DHT, and uTP
paths rather than sending them directly outside the proxy. Server-side
validation remains authoritative if a client submits an incompatible full
configuration.
The Settings API never returns the SOCKS5 password to the browser, so the password field is blank after reload. Saving it blank preserves the existing credential only while the username is unchanged; clear the username to remove authentication, or supply both a new username and password to replace it. The Network summary reports only that SOCKS5 is enabled and TCP-only/UDP-blocked; it does not display the proxy host or credentials.
Router port mapping and listener reachability
The Network view has separate cards for Router port mapping and Listen-port reachability. Both are opt-in diagnostics and controls for the configured TCP peer listener; neither a failed mapping nor a failed reachability result pauses, resumes, or otherwise changes torrent lifecycle.
Router mapping is disabled by default. In Settings > Network, enable it only
when the daemon uses strict network containment with fail-closed behavior and
a required interface. The same settings card chooses NAT-PMP and/or UPnP IGD,
optionally supplies a NAT-PMP gateway or UPnP control URL, and sets the
requested lease and renewal lead time. Saving Settings preserves these values
in the full configuration snapshot.
The mapping card shows the current state, configured protocols, local and external ports, active protocol, local gateway diagnostic, last attempt, lease expiry, and bounded detail. Refresh mapping requests an immediate reconciliation through the contained network path. The daemon renews successful leases and attempts a best-effort deletion during graceful shutdown; it never falls back to a default-route socket when a contained path or router is unavailable.
The reachability test is configured separately with an HTTP(S) endpoint the operator controls, a cache lifetime, and a request timeout. The Network card shows only whether an endpoint is configured—not its URL—alongside the open/closed/unknown/error result and cache timing. Run port test uses the same contained path and reuses a fresh cached result. A successful router mapping asks an enabled reachability test to run, but the two results remain independent: router acceptance does not prove external reachability, and a test failure does not invalidate a mapping.
If the UI shows interface_missing, the daemon cannot see the configured
interface name in its current network namespace. See
Troubleshooting.
Logs, Watch status, and doctor report
Operational diagnostics in the UI come from:
GET /api/v1/watch/statusfor enabled folders and recent watch-folder activity.GET /api/v1/logs/recentfor live-tail style log snapshots.GET /api/v1/doctorfor a consolidated operational check summary.GET /api/v1/versionfor the application version shown in the Doctor view.
The Watch history table has a separate stable Outcome column: imported,
duplicate, permanent failure, or transient failure. Duplicate means the
existing torrent was retained unchanged and the configured success action ran.
Transient failures remain eligible for a later stable scan; permanent failures
do not retry an unchanged fingerprint. The Status column is warning-colored
when post_action_error is present even if the primary outcome is imported or
duplicate, and Detail shows both the primary error and archive/delete/failure-
move error so the operator can resolve a retained source or destination
collision. Pending counts include unseen, changed, stabilizing, and transient-
retry files but exclude unchanged processed files. Watch history contains only
the current daemon run and retains its newest 10,000 results.
The Settings view also exposes a destructive Reset action. After confirmation,
it calls POST /api/v1/reset to stop torrent activity, remove torrent records,
empty the configured download and incomplete directories while preserving those
root directories, and clear daemon log files.
Browser assets
The daemon serves the Web UI favicon set and app manifest from the embedded
graphics assets. The header uses the SwarmOtter icon next to the app name and
includes a light/dark theme icon. The Web UI defaults to dark mode and stores
the selected theme in browser localStorage under swarmotter.theme.
Web assets use a self-only content security policy and cannot be framed by
another site.