Releases: icecompany-tech/iceslab
Release list
v0.1.8
Operator production-readiness, surfaced by dogfooding a real paid service on
Iceslab: kill or rotate a leaked subscription link, reset a user's traffic on
demand, and scope API tokens to least privilege. Plus multi-hop cascades
validated end to end on real nodes for the first time (RU entry to EU exit, the
client's egress IP becomes the exit's), which surfaced and closed two real
defects along the way, a read-only demo build of the panel for the landing page,
the full set of well-known client detection rules with an inline enable toggle,
a per-profile reach count that ignores deleted users, and a batch of form and
stats polish.
Added
- Revoke and rotate a subscription link. Kill a leaked or abused link (it
returns 403 until rotated), or rotate it to issue a fresh token that kills the
old link immediately. From the Users page or the API. - On-demand traffic reset. Zero a user's used traffic and lift a traffic
limit in one action (period-billing top-up) without waiting for a cron reset;
a limited user goes straight back to active and is re-provisioned. - API tokens enforce scopes. A token can be least-privilege now: one scoped
to users plus subscription-read cannot reach settings, nodes, or token
management. Existing full-access tokens are unaffected; pick a scope preset
when creating a token. - Import an existing subscription token on user create. Operators migrating
in keep their clients' current links instead of forcing a re-import. - Read-only demo build of the panel. A VITE_DEMO_MODE build serves the real
UI from local fixtures (auto-login, every change a no-op) for embedding on a
landing page, and reads ?lang= for its start language. Tree-shaken out of the
normal build. - All well-known subscription clients are seeded. The User-Agent detection
rules now cover the clients that previously had no rule and fell through to the
base64 list they cannot parse: Surge and Surfboard, Quantumult X, Loon, Outline,
XKeen, plus Karing, Throne and FoXray, and explicit plain-list rules for
Shadowrocket, Streisand, V2Box and Happ. Idempotent seed. - Inline enable/disable toggle on the subscription rules table. Turn a rule
on or off in place without opening the editor; optimistic with rollback on
failure.
Fixed
- Enabling a cascade now reaches the nodes. Creating, editing or deleting a
cascade emitted no event, so the chaining fragments only lived in the database
and never pushed to the hop nodes until some unrelated profile or binding edit
fired a re-sync. The cascade service now re-pushes every node that is or was a
hop (so disabled or removed hops also drop their fragments). - The node-agent opens the cascade link port itself. The inter-hop link
listens on a high port that install-time firewall rules do not know about, so
it previously had to be opened by hand (ufw allow from <entry-ip> ...) or the
forward was silently dropped. The panel now sends the link port and the peer
hop's address with the cascade fragment, and the agent opens UFW for it,
restricted to that peer (resolving a hostname to its IP, falling open only if
it cannot be pinned). Applied on push and re-ensured on boot. - A node reporting one user twice no longer rolls back its stats. When a node
reported the same user on two protocols (for example VLESS plus Shadowsocks),
the cumulative-snapshot upsert received a duplicate row and Postgres aborted the
whole transaction, so that node recorded nothing. The duplicates are now summed
into one entry per user. This was why cascade nodes showed zero traffic. - Per-profile user reach excludes deleted users. A profile's "users with
access" count included soft-deleted users (their squad membership is kept for
restore), so one live user could read as four. The reach query now joins live
users only. - Implausible per-poll traffic deltas are discarded. A node reporting more
than a terabyte for one user in a single 30-second poll is re-billing its
lifetime counter (an outdated agent), not real traffic; the panel now drops such
a delta and logs it instead of corrupting quotas and node history. - Form and tab polish. New profiles default to Xray (REALITY) instead of
Hysteria, the REALITY option rows bottom-align instead of staggering, the long
node bootstrap payload wraps inside its box instead of overflowing, and the
favicon is wired into the page.
v0.1.7
Changelog
All notable changes to Iceslab are documented here. Format loosely follows
Keep a Changelog; versions are git tags.
v0.1.7
The censorship-survival work from v0.1.6 made functional and field-ready: the
REALITY self-steal vertical actually works end to end now, traffic accounting is
zero-loss across restarts, and admin two-factor is hardened against code replay.
Plus the full advanced Xray option surface and an opt-in probe-resistance knob,
TLS-fragment, per-user and China routing presets, a Shadowsocks cascade link, a
realistic self-steal fallback, and live Shadowsocks user management.
Self-steal and the node-hardening toggles ship functional with real-network
field-validation in progress (the maturity bar v0.1.6 used for its experimental
features).
Security
- TOTP replay rejected within the validity window. A one-time code can no
longer be reused inside its 30-second step: the last-accepted step is recorded
and a login presenting a step at or below it is refused. Closes the small
replay window the initial TOTP work left open.
Added
- Advanced Xray options, tabbed. The profile form's Xray section moved to
tabs (REALITY / TLS / Transport) and exposes the previously hidden option
surface: REALITY xver and max-time-diff, TLS reject-unknown-SNI, XHTTP mode and
request-padding, gRPC multi-mode. - REALITY fallback rate-limit (probe resistance, opt-in). An optional
per-direction throttle on unverified fallback connections, so a scanner that
fails REALITY auth is forwarded to the target slowly and sees a slow site
rather than a full-speed proxy. Off by default: the throttle is itself a
detectable pattern, so it stays an expert opt-in. - TLS-fragment (opt-in, Xray-JSON). A subscription can fragment the client
ClientHello so SNI-based DPI cannot cleanly match the handshake. Off by
default; toggle via a panel setting or the?fragment=query. Emitted only
into the Xray-JSON format (a freedom fragment outbound the proxy dials
through); intentionally not sing-box (the upstream field is unstable). - Per-user routing override + operator custom domain lists (R3). A routing
preset can now be set per user (winning over the squad and global defaults),
and an operator can define direct / proxy / block domain lists emitted into the
Xray-JSON and Clash routing rules. Empty or unset leaves output unchanged. - China routing preset (cn-split, H2). A China-direct mirror of ru-split:
China domains and IPs resolve and egress direct (clean DNS via AliDNS),
everything else tunneled. Across Xray-JSON, Clash and sing-box. - Shadowsocks cascade link cell (C3b). A multi-hop cascade can use a
Shadowsocks-2022 inter-hop link in addition to VLESS, including mixed chains.
Node-to-node link only; the entry hop still does the DPI evasion. - Realistic self-steal fallback (G1, opt-in). A self-steal node can
reverse-proxy unverified probe requests to a real upstream site instead of the
stub page, so a deep prober sees genuine content. Off by default (static
landing); any upstream error falls back to the static page.
Changed
- Zero-loss traffic accounting. Per-user stats are now drained
non-destructively: the node reports cumulative counters and the panel computes
the delta against a stored per-node-per-user snapshot in the same transaction
as the increment. A failed or retried stats write can no longer lose a slice of
a user's traffic the way the previous reset-on-read drain could. - Frontend API base URL defaults to same-origin in production. Production
builds talk to their own origin instead of a hardcoded localhost, so the panel
works served from anywhere without a build-time override. - Dependency audit gate kept clean. Two transitive advisories pulled to
patched versions via overrides so the production audit gate stays green. - Live Shadowsocks user management (N1-SS). Adding or removing a Shadowsocks
user now goes through the running core's runtime API with no restart (live
connections preserved), matching the Xray live-management path, with the same
config-restart fallback when the runtime call cannot be made. - Stricter REALITY target check (H1). The pre-deploy test-connect now also
verifies the masquerade target negotiates HTTP/2, not just TLS 1.3, and
surfaces a single health note: a CDN-grade dest should speak both.
Fixed
- REALITY self-steal now works end to end. Two wiring defects made self-steal
silently degrade to borrowing an external TLS identity (the mode that
mismatches under aggressive DPI): the camouflage-mode field was dropped by the
config schema before it reached the node, and the per-node domain was pushed
under the wrong wire key so the node received an empty server-name list. Both
fixed, plus the node is re-pushed when its domain changes. The self-steal
vertical is now functional (real-network validation pending). - Add and remove of a user converge under rapid status changes. A user
toggled active and limited in quick succession could leave the node user set
out of sync with the panel; the sync now gates on the live desired status so
the node converges to the correct set. - Node firewall self-heals on boot. UFW was only opened for an inbound's port
inside the applyInbounds push handler, so a node that restarted (or whose rule
was lost to a reimage, or to a transientufw allowthat has no retry) could
run its core with the port closed until the next push. The agent now re-ensures
UFW for every persisted inbound on startup. Caught live: xray reachable from
abroad but the binding port was firewalled.
v0.1.6
The largest release since the alpha opened: a censorship-survival toolkit for
hostile networks (routing presets, multi-hop cascades, REALITY self-steal), live
user management with no restarts, admin two-factor auth, operator analytics and a
Telegram bot, far broader client-app coverage, and a deep performance and
reliability audit across both the panel and the node-agent.
Security
- Admin two-factor auth (TOTP). Optional RFC6238 TOTP on the admin login,
with a guided enrollment (enable requires a confirmed code, so you cannot lock
yourself out) and a disable flow. Recovery is a single SQL update if a device
is lost. Additive: existing logins are untouched until an admin opts in.
Added
- Routing presets with split-DNS. A subscription can carry a
ru-split
preset (ads and local destinations resolve and egress direct, everything else
is tunneled), rendered correctly into the Xray-JSON, Clash and sing-box
formats with a matching split-DNS block so lookups do not leak. Selectable per
subscription, per squad (override), or via a?routing=query, plus a raw
custom-rules editor for hand-written Xray routing rules. - Multi-hop cascades (experimental). Chain nodes entry -> transit -> exit:
the client connects to an entry node and traffic is forwarded hop to hop to an
exit that egresses direct. Full operator UI (hop builder, reorder, validation)
plus node-agent forwarding for the Xray vless cell. Built for networks where a
single foreign hop is blocked; field validation is in progress. - REALITY self-steal (experimental). A REALITY mode where the node runs its
own local TLS fallback and presents its own domain, so the SNI and the server
IP stay consistent (the mismatch that gets a borrowed-SNI REALITY connection
mangled on aggressive DPI). Selectable per profile. - Live user add and remove with no restart. Adding or removing an Xray or
Shadowsocks user now goes through the core's runtime management API, so live
connections are never dropped. It falls back to the previous config-restart
path only when the runtime call cannot be made, so it can only improve on the
old behaviour. - Operator analytics. Dashboard bandwidth now shows deltas against the prior
period on every window, plus a new Insights page: a subscription-request
breakdown by client app and a HWID device-count distribution, both computed
from already-stored data with no new tracking. - Operator Telegram bot. A read-only bot answering
/statusand
/user <name>to the operator chat, plus a daily digest of users near expiry
or near their traffic cap. - Signed outbound webhooks. User, profile and node events are forwarded to
configured URLs with an HMAC-SHA256 signature over the payload. - Broader client-app coverage. New subscription formats: XKeen (Xray confdir
for Keenetic routers), Outline / SIP008, Surge, Quantumult X and Loon. - Multi-core node UX. Add a second protocol to an existing node from the node
view, with an auto-picked free port and a human-readable message when a port is
already taken. Plus a masquerade REALITY recipe and a test-connect that probes
the REALITY dest for resolvability and TLS 1.3. - Per-squad defaults. A squad can carry a routing-preset override and a
default HWID device limit.
Changed
- Panel performance pass. Response schemas for fast JSON serialization on the
hot dashboard and user-list endpoints, in-process caches for subscription
settings, squad bindings and blacklist lookups (all write-busted), bulk
single-statement traffic upserts and AmneziaWG peer pre-allocation, cursor
pagination on backfill, and lazy-loaded frontend routes (initial bundle cut by
about a third). - Node-agent reliability. Adapter locks are split so a multi-second core
restart no longer blocks health checks or the panel's push workers, a bounded
restart-on-crash supervisor backs every spawned core, subprocesses are
group-killed so no orphans leak, and stats and health probes run concurrently
with cached AmneziaWG and UFW reads plus zero-user short-circuits.
Fixed
- AmneziaWG runaway traffic. AWG reported kernel-cumulative counters where the
panel expected per-poll de...
v0.1.6
The largest release since the alpha opened: a censorship-survival toolkit for
hostile networks (routing presets, multi-hop cascades, REALITY self-steal), live
user management with no restarts, admin two-factor auth, operator analytics and a
Telegram bot, far broader client-app coverage, and a deep performance and
reliability audit across both the panel and the node-agent.
Security
- Admin two-factor auth (TOTP). Optional RFC6238 TOTP on the admin login,
with a guided enrollment (enable requires a confirmed code, so you cannot lock
yourself out) and a disable flow. Recovery is a single SQL update if a device
is lost. Additive: existing logins are untouched until an admin opts in.
Added
- Routing presets with split-DNS. A subscription can carry a
ru-split
preset (ads and local destinations resolve and egress direct, everything else
is tunneled), rendered correctly into the Xray-JSON, Clash and sing-box
formats with a matching split-DNS block so lookups do not leak. Selectable per
subscription, per squad (override), or via a?routing=query, plus a raw
custom-rules editor for hand-written Xray routing rules. - Multi-hop cascades (experimental). Chain nodes entry -> transit -> exit:
the client connects to an entry node and traffic is forwarded hop to hop to an
exit that egresses direct. Full operator UI (hop builder, reorder, validation)
plus node-agent forwarding for the Xray vless cell. Built for networks where a
single foreign hop is blocked; field validation is in progress. - REALITY self-steal (experimental). A REALITY mode where the node runs its
own local TLS fallback and presents its own domain, so the SNI and the server
IP stay consistent (the mismatch that gets a borrowed-SNI REALITY connection
mangled on aggressive DPI). Selectable per profile. - Live user add and remove with no restart. Adding or removing an Xray or
Shadowsocks user now goes through the core's runtime management API, so live
connections are never dropped. It falls back to the previous config-restart
path only when the runtime call cannot be made, so it can only improve on the
old behaviour. - Operator analytics. Dashboard bandwidth now shows deltas against the prior
period on every window, plus a new Insights page: a subscription-request
breakdown by client app and a HWID device-count distribution, both computed
from already-stored data with no new tracking. - Operator Telegram bot. A read-only bot answering
/statusand
/user <name>to the operator chat, plus a daily digest of users near expiry
or near their traffic cap. - Signed outbound webhooks. User, profile and node events are forwarded to
configured URLs with an HMAC-SHA256 signature over the payload. - Broader client-app coverage. New subscription formats: XKeen (Xray confdir
for Keenetic routers), Outline / SIP008, Surge, Quantumult X and Loon. - Multi-core node UX. Add a second protocol to an existing node from the node
view, with an auto-picked free port and a human-readable message when a port is
already taken. Plus a masquerade REALITY recipe and a test-connect that probes
the REALITY dest for resolvability and TLS 1.3. - Per-squad defaults. A squad can carry a routing-preset override and a
default HWID device limit.
Changed
- Panel performance pass. Response schemas for fast JSON serialization on the
hot dashboard and user-list endpoints, in-process caches for subscription
settings, squad bindings and blacklist lookups (all write-busted), bulk
single-statement traffic upserts and AmneziaWG peer pre-allocation, cursor
pagination on backfill, and lazy-loaded frontend routes (initial bundle cut by
about a third). - Node-agent reliability. Adapter locks are split so a multi-second core
restart no longer blocks health checks or the panel's push workers, a bounded
restart-on-crash supervisor backs every spawned core, subprocesses are
group-killed so no orphans leak, and stats and health probes run concurrently
with cached AmneziaWG and UFW reads plus zero-user short-circuits.
Fixed
- AmneziaWG runaway traffic. AWG reported kernel-cumulative counters where the
panel expected per-poll deltas, so a peer's lifetime total was re-billed on
every poll and drained quotas. The agent now emits true deltas (baseline on
first sight, so an agent restart never re-bills the backlog). - Editing a limited or expired user no longer fails. Saving such a user
returned 400 on every attempt; it now reactivates correctly, and a 0 GB
traffic limit is read as unlimited. - Per-user stats no longer error on multi-inbound users. A user present on
more than one inbound of a node tripped a Postgres conflict (21000); per-user
rows are aggregated before the bulk upsert. - Smaller audit fixes. IPv6-aware subscription host parsing, a human-readable
port-conflict 409 naming the node and profile, an online-aware node status dot,
a flag-emoji guard for non-ISO country codes, a bounded Hysteria auth-callback
body, and a settings form that re-seeds from the server after save.
v0.1.5
v0.1.5
Full Xray protocol matrix: VLESS, VMess and Trojan over any transport and any
security mode, behind a guided picker. Plus an update-available indicator and a
round of VPS hardening.
Security
- nginx ships hardening headers. X-Frame-Options, X-Content-Type-Options,
Referrer-Policy and server_tokens off on every response. CSP is left to the
operator so a wrong policy can't silently break the SPA.
Added
- Full Xray protocol matrix. A profile can now run VLESS, VMess or Trojan over
any of the six transports (raw, WebSocket, gRPC, xHTTP, HTTPUpgrade, mKCP) and
any security mode: REALITY, plain none (for a CDN that terminates TLS itself), or
node-terminated TLS with your own certificate. A guided three-step picker reveals
only the fields each combination needs, emitted correctly into the raw/base64,
Clash, sing-box and Xray-JSON subscription formats. - Update-available indicator. The sidebar shows an accent dot linking to the
release when a newer version ships. Best-effort GitHub-release check (cached 6h,
never blocks a request, no token needed on the public repo).
Changed
- Subscription formatters are security-aware. Clash, sing-box and Xray-JSON
hardcoded REALITY for every Xray endpoint, so a none/tls profile would have
produced a broken client config. They now render the correct security block per
endpoint and carry all three subprotocols. - VPS resource and secret safety. Redis is capped (--maxmemory + noeviction),
and deploy.sh snapshots .env.production (the only on-host copy of secrets + node
mTLS CA) to a timestamped backup ring before each deploy.
v0.1.4
v0.1.4
Reliability hardening after a deep code audit: the bug-fix campaign, a deploy/
update fix so operators stop getting stuck on stale code, and a round of UI polish.
Fixed
- node-agent no longer stalls during a core restart. Adapter mutexes were
held across the multi-second subprocess restart (xray / shadowsocks / mtproto /
mieru / naive), blocking/healthzand the panel's push workers, plus a data
race on the Hysteria auth-callback. Locks are split so restarts run lock-free. - deploy/update no longer silently rebuilds stale code.
deploy*.shran
git pull --ff-only, a no-op on the tag-pinned detached HEAD the installer
leaves behind, so re-deploys quietly rebuilt the old version. They now sync to
ICESLAB_REF(branch or tag), fetch all refs, and fail loudly on an ambiguous
detached HEAD. The installer is a full clone so updates stay reachable. - deploy applies new migrations. Migrations ran against the previous image
before the rebuild, so a deploy that added a migration silently skipped it.
Reordered to build, then migrate, then start. - multi-profile deploy to a fresh node. Deploying several profiles to a new
node assigned port 443 to every one, so all but the first failed with
PORT_IN_USE. Each profile now gets a distinct port. - "Top users today" dashboard card was always empty (the table it reads was
never written); the stats poll now records per-user daily usage. - Subscription endpoint name de-dup, inline port-edit collision check, bounded
AmneziaWG IP allocation, and other audit fixes.
Changed
- Protocol dropdown lists Xray first with a disabled "sing-box (soon)" teaser
everywhere a protocol is chosen. - UI polish. The Users status chips are now the single filter (dropped a
duplicate control); node cards show the node address; filter chips are
keyboard-operable. - Build resilience.
prisma generateretries on a flaky network during the
Docker build, and the Prisma update-check call is disabled.
v0.1.3
v0.1.3
Subscription self-service for end users, plus a dashboard CPU-reporting fix
and an ops-script papercut.
Added
- Human-readable subscription page. Opening
/sub/<token>in a browser
used to dump raw base64 (#1).
It now serves a self-contained landing page: status (traffic / expiry /
protocols), a copy-able subscription link, deep-link import buttons
(Hiddify / Streisand / v2rayNG / Clash), and per-format download buttons
including the AmneziaWG.conf. RU/EN by Accept-Language. VPN clients are
unaffected (an explicit?format=always wins). - QR codes on the subscription page. One QR for the subscription URL
(scan to import in proxy clients) and, when an AmneziaWG endpoint exists, a
QR of the wg-quick config text (scan straight into AmneziaVPN). Generated
server-side as inline SVG viaqrcode-svg(zero external requests).
Fixed
- Dashboard CPU headline. The host CPU card showed a 200ms instantaneous
sample taken while the backend builds the overview, so a 1-vCPU host saw
its own work as an 80%+ spike. The headline now uses the 1-minute
load-average percentage (sustained busy-ness); the sample stays as
secondary detail. - Ops scripts run from anywhere.
deploy.sh/restore.sh/ etc. errored
when run fromscripts/instead of the project root. They now auto-resolve
the root, socd scripts && ./deploy.shworks too.
Performance
- Dashboard overview cache TTL 8s → 30s. At 8s the cache expired before
almost every poll, so the ~20-query recompute ran every ~10s and pegged
small hosts. Now throttled to at most twice a minute regardless of tab count.
v0.1.2
fix(version): bump panel package.json 0.1.0 -> 0.1.2 The UI version badge reads __APP_VERSION__, injected from package.json at build time (vite define). The version field was never bumped on the v0.1.1 / v0.1.2 releases, so the panel logo / login / dashboard all showed v0.1.0. Bump frontend (drives the UI) + backend to 0.1.2.
v0.1.1 — post-publish stabilization
v0.1.1 - post-publish stabilization
Released 2026-05-20, ~36h after v0.1.0 went public on 2026-05-19.
═══ LIVE-INCIDENT FIXES (wave-12) ═══
First public VPS smoke on Aeza 2GB caught:
- docker-compose healthcheck path mismatch (was /healthz, panel serves /health)
- pnpm install OOM on 2GB VPS (added auto-swap, NODE_OPTIONS heap-cap, npm retries)
- V8 auto-detected 60MB heap on 2GB VPS, runtime now caps at 512MB
- Dashboard "process memory %" was denominating against heapTotal not heap_size_limit
- Login-lockout DoS: bots locked admin account, legit operator couldn't get in.
Relaxed defaults to 10/5/10 (was 5/15/15).
═══ SUBSCRIPTION + UI REFACTOR ═══
- Subscription promoted to its own sidebar group with /metadata and /routing pages
- Sidebar now has Workspace / Subscription / System group labels
- /srr → /subscription/routing with backwards-compat redirect
- Settings slimmed: removed Auth-methods vapor card (Passkey/Telegram/GitHub/OAuth2)
- Theme fix: filled Buttons without explicit color rendered light-on-light (unreadable)
- Unified CTA buttons across Settings + Subscription Metadata to PrimaryButton
═══ USERS FORM ═══
- All-squad checkbox now toggleable; yellow warning on both-checked
- Status Select localized (was hardcoded English Active/Disabled)
- ProfileHeader status Badge pipes through t() for localization
- usersEyebrow plural-aware via i18next (1 аккаунт / 5 аккаунтов)
═══ PROFILE / SQUAD FORMS ═══
- validateXrayConfig refactored to return i18n keys (was hardcoded RU)
- Shared lib/protocols.ts with PROTOCOL_OPTIONS, used by both modals
- Default All squad name+description localized (DB seed stays English)
- Per-protocol divider shows friendly name not raw enum
- Many label localizations across Hysteria/AWG/NaiveProxy non-spec fields
═══ MTPROTO FIXES ═══
- tg://proxy URI no longer carries #fragment (TG iOS rejected with "Invalid proxy link")
- Presence-only online state: cron treats adapter tracking as online signal for
protocols where per-user metrics are architecturally impossible (mtg) - UI hint: yellow ⓘ badge in DIRECT PER-PROTOCOL LINKS explaining the gap
═══ i18n POLISH ═══
- Removed all em-dashes from locales (AI-tell, looks generated)
- Dropped "saved on commit" jargon eyebrow
- Localized topbar Search placeholder, Users-status chip Off → Откл
- pageHero.srrTitle "Subscription Rules." → "Маршрутизация." on RU
- Many code-switching cleanups (inbound'ов → inbound-ов, etc)
═══ OPS SCRIPTS ═══
- New scripts/_lib.sh with shared helpers (colored log_info/ok/warn/err,
numbered [N/M] steps, ERR trap with context, require_compose_root) - All 7 ops scripts (deploy*/cleanup/logs/backup/restore) use the shared helpers
- deploy.sh now ff-only pull + shows git short SHA before/after
- iceslab-backup.sh: BGSAVE wait fails loudly on timeout (was silent)
- CRITICAL: iceslab-restore.sh service names corrected (was panel-backend,
actual is backend), restore was effectively broken before
═══ INSTALL-NODE.SH ═══
- Fixed stray '@' in XTLS/Xray-install invocation (caused unknown-option abort
on xray/shadowsocks protocol install)
═══ DOCS ═══
- README + README.ru: Project policies section linking CONTRIBUTING/SECURITY/TRADEMARK
- install-iceslab*.sh scripts grew time-tracking + ERR-trap (wave-12)
═══ KNOWN ISSUES (wave-13) ═══
- Per-username lockout is a DoS vector (plan: per-(IP+username) + CAPTCHA)
- Public IP draws bot scans within 12h (plan: fail2ban jail in node installer)
- handleAddUser fanout requires all-success across 7 adapters
- lastStatusMessage can stick around after healthcheck-poll transitions
- install-iceslab.sh interactive prompt sensitive to UTF-8 backspace
v0.1.0 public alpha
First public release. Multi-core proxy panel.