Repository navigation
0.58.0
Altnautica Command v0.58.0
This release covers everything since 0.57.3, about six weeks of work. Two
threads run through it.
The first is reach. A drone that can only be heard through a ground station's
radio is now a first-class node: it gets the whole agent surface, every
configuration page, extension installs, cockpit video and live vision
detections, and it commands with the vehicle's own acknowledgement coming back.
Above that sits a Swarm tab for flying a fleet from one screen, and below it a
credential model where an operator holds their own expiring, device-scoped
broker grant instead of a shared read-only one.
The second is honesty. Nine protocol constants addressed the wrong message or
sent the wrong command. A waypoint altitude meant two different things depending
on which surface read it. The cockpit painted a dead link as live telemetry, the
HUD drew a level horizon with no attitude data, and the badge whose only job is
to announce a stale link could never appear. A browser holding a receive-only
relay credential showed a healthy commanding link while the broker discarded
every frame it sent. All of that is corrected, and most of it is now provable in
demo mode without hardware.
Added
- A Swarm tab, a fourth fleet-overview surface beside Grid, Overview and
Nodes, for flying a multi-drone fleet from one screen. Five bands in
summary-then-exceptions-then-detail order, because 20+ healthy rows have to
visually disappear for the unhealthy one to be findable: a severity summary
strip whose chips filter the table, a fleet action bar (Hold, RTL, Land,
Set-Formation), a dense one-row-per-slot table sorted unhealthy-first, a
Leaflet fleet map with marquee select, and a video rail. Arm and Disarm are
deliberately absent from the bulk actions: arming is the one action whose blast
radius grows with the selection. - Each row renders separate glyphs for armed, guided, emergency and GPS
rather than blending them into one colour, since each is an independent
condition, and shows the currently active arbitration level rather than the
commanded mode — a drone whose separation layer has taken over reads
hard-separationeven while a formation is commanded. - Make Hero is a new primitive, not a rename of Pin. Pin is multi-select,
personal and bandwidth-neutral; Hero is exclusive and changes the aircraft's
actual radio allocation by demoting the previous hero to a 1 fps thumbnail. It
has its own verb and icon, and the UI renders the beacon's own hero bit as
truth, so a failed demotion is visible rather than assumed. - Fleet-wide actions carry two independent gates: the existing typed-phrase
confirmation plus a broadcast toggle that must be armed first and self-reverts
to per-selection after five seconds. Alerts aggregate rather than enumerate —
"3 drones low battery" is one chip, not three rows. swarm-beacon-storeholds live neighbour beacons keyed by fleet slot, fed by a
2 Hz bridge against the ground station's/api/swarm/neighborswith back-off
when a host does not answer. Deliberately not persisted: a beacon with a
three-second shelf life rehydrated from disk would render a dead aircraft's
last position as current. A slot the fleet registry knows about but that is
currently silent gets its own row rather than vanishing from the board, and an
unprovisioned node reads as unprovisioned rather than defaulting to fleet 1.- A Radio settings page under Link & network, carrying fleet addressing, the
link switches, and modulation. The fleet slot is read-only because the ground
station assigns it and hand-editing it degrades the shared channel for every
node on it. The modulation band shows the manual rung only while the adaptive
ladder is off and otherwise a read-only live readout of the applied rung and
measured SNR, so the operator always sees which rung is actually live. No
channel-width control, because the transmitter is pinned to 20 MHz. - The per-node Swarm settings page is expanded in place: formation is now a
closed set of five built-ins instead of free text (a typo previously produced
no formation at all, silently), flocking gains sit behind an Advanced
disclosure with their applied float stated, the two separation values are held
behind a confirmation because they are the safety envelope, task allocation
shows the assignment and not the algorithm, and the arbitration ladder is shown
read-only. - A drone reached only over a ground station's radio now gets its whole agent
surface. Clicking it connects, the Agent tab and its sub-nav render for real,
every configuration page resolves through the relay instead of reporting that
the node configuration could not be read, the Status tab's companion band
appears, the Extensions tab lists what is installed and can install over the
relay, and the cockpit gets the ground station's video and polled live vision
detections. Commands return the vehicle's own acknowledgement, and a "WFB
relay" transport label appears wherever the reach chip is shown. Log tailing
and the camera roster stay deliberately unavailable over the relay rather than
quietly answering with the ground station's own data. - Command authority is now a first-class state, separate from a socket being
open. The flight-controller uplink publishes at a quality of service that
acknowledges nothing, so a browser holding a receive-only broker credential
kept a transport that read healthy while every frame it sent was discarded with
no error to catch. The cockpit safety band gains a CMD chip that appears only
while authority is limited or ending (RECEIVE ONLY,AUTHORISING,ENDING SOON), the flight data card's Link dot is graded on whether a command can
actually be published and explains itself in one line underneath, and the
sidebar row, fleet tile, nodes-board reach cell, node-detail header and the
Overview parameters tile each carry the same badge. The health ring is left
alone: a node can be alive and uncommandable at once, and one channel cannot
carry both. - Per-operator broker write grants. An operator requests command authority
from the drone overview and receives their own broker principal, scoped
server-side to the drones they own, expiring on its own after an hour, renewed
ahead of expiry, revocable instantly, and confirmed by the first publish the
broker actually accepts. The plaintext secret is returned once at mint and
never persisted — the database keeps only a verifier, the same bar the broker
itself holds — and it is deliberately kept out of store state so no persist
wrapper or devtools serialiser can pick it up. - A World Model generation card. The paired compute node publishes a
descriptor stream for each completed reconstruct generation, and the GCS now
subscribes to it and renders the generation's four artifact slots (Gaussian
splat, point cloud, mesh, occupancy grid) in the drone's world-model surface
and in the Atlas setup surface. A slot the generation did not produce stays
absent rather than rendering an empty one, silence renders as "no world model
was produced" rather than as a surveyed-and-empty world, and every stand-down
reason is its own stated status. The page appears only when the node's own
config surface advertises the world-model block, so an agent that predates the
feature shows nothing rather than an empty page. - Image orientation on the Video settings page: rotation in 90° clockwise
steps plus horizontal and vertical flip, applied on the encode pipeline before
compression and taking effect at the next pipeline start. - Demo mode covers the new surfaces. A 14-slot simulated fleet with formation
geometry, a synthetic swarm bus, hero promotion and fleet-wide config fan-out
makes the whole Swarm tab explorable with no hardware. The iNav demo vehicle
now renders the flight-controller configuration surfaces its firmware badge
advertises — mixer and servo rules, per-timer output modes, temperature
sensors, serial ports, battery profiles, OSD layouts and EzTune — and a save
then reload round trip behaves as it does on hardware. Demo mode can also
produce a genuinely stale link on demand, which is what makes every
freshness-gated surface verifiable offline; the staleness bugs fixed in this
release previously only appeared on a real link loss in the field. - New plugin permissions and contracts:
msp.readandmsp.writefor MSP
flight controllers, the second marked high risk because it can drive the RC
channels and arm the aircraft;vision.model.readfor a plugin's own resolved
model delivery status; ajanitorsidecar carrying disk-janitor footprint,
budget and reclaim state; awfb-pair-proofsidecar carrying whether a radio
key has ever been confirmed to carry a link and the remaining auto-pair re-arm
budget; and anaux.link_feedbackcontract so ground-measured video link
quality reaches the transmitting drone and its bitrate ladder steps on a real
loss sample instead of the sentinel a transmit-only radio can never leave.
Changed
- The node Agent page is one sub-nav instead of three stacked sidebars. All
seventeen configuration pages are now entries beside the live surface for the
same subsystem, twenty-five entries across Overview, Link & network, Video &
vision, Cloud & remote, System & safety and Software, with an empty section
omitted entirely. The Atlas page reads "World model setup" and the vision
settings page reads "Perception setup". - The Video settings page is camera, encode and orientation only; every
video.wfb.*field moved to the new Radio page. Video is offered on drone
profiles alone — a ground station encodes nothing, and its radio fields live on
the page it does get. A profile that fills none of a settings group no longer
renders that group's header. - Settings → Extensions is a read-only fleet overview of what is installed
per node. Its registry cards open a node picker instead of installing, and the
install-from-URL affordance is gone from that page; installing is done from the
node you are installing onto. The first-party registry entries now carry a real
checksum, size and supported-board list, and the stale download URLs are fixed. - Fleet formation and mode changes ride the config path, fanned across the
selection, rather than the command catalog: that catalog is a closed set the
agent enforces before any frame is sent, and the config path already has all
three transports. - The desktop app tells you the truth about updating itself. The update check
still runs and still reports a newer version, but install-on-quit is armed only
where it genuinely works. macOS builds are unsigned and un-notarized and the
macOS updater validates an update's signature against the running app, so on
macOS — and on Linux started outside an AppImage — the app now points you at
the releases page instead of accepting an install that could never happen.
Windows and AppImage Linux are unchanged. Download and install failures are
reported rather than discarded: the updater's logger was previously set to null
and the check's rejection swallowed, which made a broken updater
indistinguishable from an app that was already current. - Video latency is measured per hop instead of assumed. The old single number
was network round-trip plus the decoder's jitter wait, which is not the same
quantity as end-to-end latency and contained no part of the capture or encode
leg. Each hop is now derived from the timestamps the browser supplies per
rendered frame and carries its provenance — measured, RTCP-synchronised,
derived or user-agent-estimated — and the two hops no browser API can see are
named as unavailable rather than left as a gap that reads as zero. The jitter
buffer target is a closed loop over what the receiver actually measures rather
than a hardcoded 50 ms, and where the agent injects a frame timestamp the app
reports true glass-to-glass. - Radio airtime is spent deliberately. The poll backoff ceiling is a multiple
of each lane's own cadence rather than one absolute wall, so an unreachable
relayed node settles at 100 s instead of retrying on the LAN's 30 s wall; the
vision-detection poll steps out while nothing is being tracked and snaps back
on the first fresh batch instead of running at a fixed 4 Hz; and the relay
timeouts are layered so the ground station's real error is what surfaces
instead of a client abort. - Each cockpit corner has one placement system. Widgets previously either
self-anchored or were placed by the host, so two could collide in the same
corner and an operator's layout override only worked for some of them. Every
arrangeable widget is now grouped into one of nine zones plus a full-bleed
centre layer, so it can be moved between corners and hidden per loadout. The
fixed instrument HUD and the edge tapes still self-position. - More of the app speaks the operator's language. The node console — node
rows, badges, liveness and role labels, the node actions menu, and the flight,
jobs and status cards — is translated across all sixteen locales, as are the
command-authority badges, the cockpit corner alerts and the stick-block
warning. Protocol and product nouns stay identical in every locale on purpose.
The cockpit safety-band strings ship English in every locale for now: a safety
string translated wrongly is worse than one visibly untranslated.
Fixed
- Nine protocol constants addressed the wrong thing. Five messages were keyed
to another message's id, so every frame of them failed CRC or was skipped as
unknown and the surface above stayed empty with no error:EKF_STATUS_REPORT
335 → 193,GIMBAL_DEVICE_ATTITUDE_STATUS284 → 285,
GIMBAL_MANAGER_INFORMATION285 → 280,GIMBAL_MANAGER_STATUS286 → 281, and
AIS_VESSEL246 → 301, which had been keyed to the id ofADSB_VEHICLE. Four
commands sent a different instruction than the label: "bind receiver" sent
PREFLIGHT_UAVCAN, whose first parameter triggers a one-time DroneCAN actuator
ID assignment, so pressing it re-enumerated the CAN actuators instead of
binding anything; one of the two fence-enable paths sent a number that is not
in the command enum at all, so an operator could believe the geofence was armed
when it was not; commanding a gimbal angle left the mount mode at RETRACT, so
it stowed the gimbal instead of pointing it; and a parameter reset passed −1 as
the mission-storage action, outside the enum, where 0 means no action. Four
canonical payload lengths were also pinned at their pre-extension size, so the
parser's zero-restore stopped short and the extension fields had no source:
GPS_RAW_INT30 → 52 (which is why GPS-for-yaw reported nothing),
BATTERY_STATUS36 → 54,DISTANCE_SENSOR14 → 39, andCOMMAND_ACK3 → 10. - A waypoint altitude means one thing everywhere. A
relativealtitude is
measured above the home point, and several surfaces read it as above ground
level. Over terrain that rises between home and a waypoint, a healthy-looking
50 m then sits below the ridge in front of it, which made an unsafe mission
validate clean and draw as safe. There is now one resolver every consumer goes
through — validator, terrain profile, flight path, mission expansion, import
and export — mapping the three frames onto their real datums and returning
nothing rather than a fabricated zero when the datum it needs is missing. Saved
missions are not rewritten and each waypoint keeps its own frame, so a mission
uploads exactly the frames it was saved with; what changes is that the GCS now
flags and draws it correctly. - The command queue no longer cancels itself, and a parameter write no longer
lies. Pending commands were keyed by command id, so a second in-flight
command with the same id superseded the first: the adapter fires
REQUEST_MESSAGEthree times back to back on connect, and two of the three
were cancelled before the vehicle could answer. Entries are keyed by a
monotonic ticket now, with same-id acknowledgements resolving the oldest match
because the protocol carries no correlation id, and a cap of 32 in flight makes
a caller that loops without awaiting fail loudly. An acknowledgement addressed
to a different ground station on a shared link no longer resolves our command.
setParametergraded success on reaching the wire; it now grades on the
vehicle's own echo matching the value written, retries three times before
calling a dropped write a rejected one, and releases its subscription on every
exit path — the timeout path used to leak one permanent callback per failed
write, one per parameter on a bulk save over a lossy link. Reconnecting through
the same adapter double-dispatched every frame; the frame handler is released
on disconnect. The send-sequence counter is per sending system and component
rather than one global, which is what makes a firmware's packet-loss figure
meaningful once a second sender exists. - Commands that could act on the wrong vehicle, or act unasked, are gated.
ArduPilot's vendor calibration range went out on any firmware, so on PX4 and
the MSP firmwares the calibration wizard waited on an acknowledgement that
meant nothing; each path now refuses with the connected firmware named.
CompassMot had a second entry point that bypassed the firmware check, and it is
gone. The kill switch is flight termination and irreversible in flight, so the
protocol layer now refuses it unless the caller states the operator confirmed
it. Guided goto was a raw socket write reporting success for reaching the
socket, with coordinates squeezed through 32-bit floats; it is an
acknowledgement-tracked integer command that keeps full precision. The MSP
motor test accepted a duration and dropped it — the MSP motor command is a
level, not a pulse, so the motor held its last written value; the stop is now
scheduled per link, cancelled on disconnect, and the test is refused while
armed. - Commands are aimed at the link that can carry them. With a vehicle
reachable over more than one link the send target was chosen by byte recency,
and on the cloud relay the receive-only downlink pushing telemetry is easily
the busiest, so every command was published into a broker that discarded it.
Liveness and commanding are now separate notions, and a link that cannot
command still connects and still streams telemetry rather than failing its
connect on housekeeping frames it cannot send. - The cockpit stopped painting a dead link as live telemetry. Every telemetry
ring keeps its last sample forever, and the safety band read it with no
freshness gate despite its own documentation claiming otherwise, so the last
battery percentage, GPS fix and signal bars stayed on screen for as long as the
tab was open after a link loss. Readings now collapse to absent past five
seconds and each surface renders the honest placeholder it already had. The HUD
drew a perfectly level pitch ladder at zero pitch and zero roll with no
attitude data at all — a confident straight-and-level indication produced by a
null-coalesce — and now draws the fixed reticle without the earth-referenced
marks; overlays receive no attitude rather than a permanently wings-level one.
The LINK STALE badge, whose entire job is to appear when telemetry stops, could
never appear, because its age check sat in a memo with no time dependency and
nothing re-rendered once the link died; it rides the shared clock now, the
battery and fence badges are freshness-gated with it, and it stays suppressed
on a session that never had a link. The flight clock restarted at zero when the
cockpit remounted, so switching to the map and back reset the timer mid-flight. - The signing panel no longer implies it verified anything. No message
authentication code is checked on the receive path, so the received-frame
counter reads "RX signed (observed)" and is documented as observed; the
enforcement banner appears only on a real readback of the flight controller's
requirement rather than on an unknown; and the status row states the actual
enrollment state instead of implying enrollment. - The stick path stopped being able to fly the aircraft by accident. Reading a
gamepad and transmitting an RC override were one function, which meant opening
a keybinding panel opened an override on the connected aircraft; they are two
lifecycles now, and only the cockpit and HUD start the transmitting one. Every
frame re-checks an allow list, so transmission stops the moment the aircraft
disarms or the mode leaves pilot authority, a mode nobody classified blocks
rather than opens, and the opt-in defaults off and is revoked when the
controller drops so re-attaching a pad never silently resumes flying. An MSP
link now keeps one authoritative picture of every RC channel: throttle idles at
its low end rather than centre, an AUX channel nobody wrote rests at a value
proven to activate none of the modes configured on it — a resting value inside
the arm range arms the aircraft — the frame width is sized once at connect and
can never narrow, and a motor cut is latched and re-emitted rather than lasting
one frame. When a flight controller will discard override frames because its
receiver is not MSP, the Controllers section says so and says nothing is being
transmitted, instead of leaving a live-looking stick display and silence. The
stream transmits at the rate the link declares and puts nothing on the wire for
a link declaring none, where it previously transmitted at a hardcoded 50 Hz
regardless. Gamepad buttons reach their subscribers, which is what makes
binding capture and the skill radial respond to a press. - iNav navigation commands do something. Return-to-home, takeoff, pause and
resume refused unconditionally on every MSP firmware while the skill bar still
offered them, so an operator could confirm a return-to-home and have nothing
happen. Each drives its configured AUX switch now, and a refusal names what is
missing: a firmware with no navigation modes says so, an aircraft with no
switch assigned names the mode and points at the modes tab, and land explains
that iNav lands as the last leg of return-to-home. - An MSP vehicle's flight mode is decoded against its own firmware. Every MSP
aircraft was decoded against Betaflight's box numbering, where iNav's
navigation boxes are absent or mean something else, so an aircraft in a
navigation mode could display as a manual one — the dangerous direction,
because the stick-authority allow list would then let a gamepad override
transmit at an aircraft the autopilot is flying. An unidentified firmware now
falls back to unknown rather than to acro. - Encoders refuse an out-of-range value instead of narrowing it. 256 written
to a byte becomes 0 and 70000 written to a 16-bit field becomes 4464, each a
plausible-looking value on the wire, so a caller's mistake reached the aircraft
as a different instruction rather than as an error: waypoint 256 addressing
waypoint 0 and overwriting the first item of a mission is the shape of it. The
manual-control, mission-item and iNav waypoint encoders now throw with the
offending field named. - One video stream, one session. Four surfaces reach the same stream — the
cockpit canvas, the focused-drone card, the HDMI kiosk background and the
context rail — and each opened by closing any existing connection, so mounting
the second blacked out the first and either unmount tore down the survivor. A
session is now held by a lease count: joining a live stream returns the same
media stream, joining one still negotiating joins that negotiation, and only
the last release tears it down. - A relayed drone's fleet-card fields populate. CPU, memory, disk,
temperature, board, uptime, agent version, camera and video state, flight
controller identity and the service counts read blank because the parser read
short wire keys that never leave the radio process while the ground station
serves the long-key expansion. Opening a relayed node also retries a failed
connect instead of freezing on a link that loses roughly one uplink datagram in
three, an HTTPS page blocking a plain-HTTP relay request says so rather than
timing out as an unreachable drone, and logging health fields a relay cannot
observe report absent rather than false. - A broker that refuses a video signaling offer says so immediately, instead
of the offer hanging for thirty seconds and then blaming the agent. The offer
is published at a quality of service that returns a reason code, and the code
was being discarded. - Persisted state survives a browser with no IndexedDB. Every persisted store
threw as its middleware hydrated in Safari private browsing, which refuses to
open a database; storage now degrades to memory for the session with one
warning, because a store that forgets is the truth where a throw is a crash.
The UI-preferences store also gained the migration handler it was missing, so
the next version bump cannot wipe every remembered per-node tab through a throw
inside rehydration. - The fleet has one write path and the selected drone one owner. Two writers
produced a race that could lock the agent tabs and show "No Drones Connected"
while a direct connection was live; the node registry is the single write
target and the fleet view is projected from it. A duplicate selection mirror
that failed to propagate a deselect is gone, which is what let a background
vehicle's frames interleave into the shared telemetry rings. - Every theme has a legible button and every control a visible focus ring.
Controls painted on the accent colour hardcoded white text, which fails WCAG AA
against eighteen of the twenty-two accent colours in the stylesheet and reaches
1.37:1 on the night-vision theme, where white also breaks dark adaptation. Each
theme now states its own accent foreground and a test fails the build below
4.5:1. The old focus ring was a box shadow painted against the control's own
accent background, so it was invisible on exactly the buttons a keyboard
operator most needs to find; one outline utility replaces it, drawn outside the
border box and only for keyboard focus. - The cockpit obeys the selected theme, night vision above all. It hardcoded
its entire palette, so the one surface an operator flies from stayed bright
blue and amber after selecting the night-vision theme whose only purpose is
preserving dark adaptation. The palette is now themed, and night vision derives
the whole HUD from the theme's green. - The desktop app can still reach a LAN agent on current Chromium, which
denies local-network requests unless they are asked for; without the permission
a drone on a LAN address or a.localhost read as a network outage rather
than as a denial. Origin gating is unchanged. - The broker credential is no longer served to anyone who asks. A public
query with no authentication check returned the shared broker username and
password in its payload to any caller who could reach the deployment, and that
principal reads every topic for every drone in the fleet. It is gone from the
client entirely, replaced by the per-operator grants above; plugins lose that
route to it as well, with no allowlist change. - Agent credentials are no longer handed to unauthenticated callers. Agent
registration was a public mutation, so any browser could write an
attacker-chosen agent key against any device id and drive the auto-match branch
as fast as it could guess six-character codes; it is now reachable only through
its HTTP route, requires a non-empty key, and re-registering a device that
already holds one requires presenting it. Redeeming a pairing code returned the
agent's API key, which the browser never needed; it no longer returns it on any
path. The pairing-status route now demands the device's own key and answers
identically for an unknown device and a wrong key, so it cannot be walked to
enumerate device ids. Anonymous ownership was a client-asserted argument, so
knowing another browser's id was enough to claim its nodes; it is derived from
a server-minted session whose secret is stored only as a hash. Every anonymous
entry point now consumes a rate-limit attempt with an escalating, never
permanent lockout, and a drone re-presenting its own key is free so a real
aircraft cannot ladder itself out. - An empty agent key no longer authenticates. A drone paired before its agent
connects was stored with an empty key, and the check was a plain inequality, so
a caller presenting an empty key authenticated as that drone and could patch
fields feeding an operator-facing reach surface. A blank on either side is
refused, comparison is constant-time at all six checks, and the heartbeat
mutation is internal so it cannot be invoked directly from a browser and skip
the route's validation. - Twenty-eight dependency advisories cleared, three of them critical, among
them an email-normalisation bypass in the authentication core that let a
homoglyph address pass as another account's, and a path type confusion in an
archive library. Notable moves: Next 16.3.4, Electron 44.1.1, electron-builder
26.15.3, electron-updater 6.8.9, the Convex auth package pinned at 0.0.95. The
schema validator the app runs on every agent response was also a phantom
dependency, resolved only through a lint config's transitive tree, so an
install without dev dependencies would have broken the production bundle. - A session expiry no longer blanks the command screen. One query per
in-flight cloud command resolved ownership without a skip guard, so an expired
session threw out of render into the error boundary. The MQTT bridge also had
no error listener, and the client library rethrows an unhandled error as an
uncaught exception, so a broker outage escaped the component; control-plane
topics moved to a quality of service that acknowledges, a refused subscribe
stops claiming the surface is connected, and teardown drops its listeners
instead of leaking a set per node switch. - Nineteen heartbeat fields reached the cloud path again. The status route
picks fields explicitly and had drifted behind the writer, so fields that had
been added and given a column were accepted, declared, stored and never
written: the LCD legs, the local-decoder and recording legs, theme and display
type, the plugin-update check, the video stream list, the CAN bus list and the
vision legs. For a self-hoster that is the LCD tab, the CAN rows, the vision
panel and the stream switcher reading empty over the cloud while the same agent
fills them over the LAN. Five reconciler verdicts the agent already folds in —
management-link health, reach-back mode, USB rehome, Wi-Fi power save and MAC
stability — are picked up on the LAN transport too; MAC stability had six
readers and three cards and no transport that produced it.
Removed
- The Pipeline buffer row in the video latency breakdown, and the field
behind it. The agent's producer was a query on a pipeline that no longer
exists, so the row rendered a permanent dash beside live ones. The neighbouring
tooltip is corrected: it measures capture to readback on the drone, not the
pipeline buffer. - Five video encoder-identity fields and the pipeline pill they fed. Nothing
on the agent side produced them and nothing here read them. - The MAVLink
RC_CHANNELS_OVERRIDEencoder. It had no adapter method and no
caller, which makes it read as a tested control path when it is not. The live
stick paths are unchanged, and inbound frames of that message still decode and
display. - The
hardware.audioplugin capability, which no longer appears in the
permission catalog. - The shared read-only broker credential, in favour of per-operator grants.
- The generated agent API type mirror, its generation script and the
dependency that existed only for it. Nothing imported it, which is why it
drifted from 131 declared paths to the agent's real 54 unnoticed. A vendored
node_modulestree of about 46 MB undertools/is untracked too, so a clone
is that much smaller.
Notes
- Pairs with ADOS Drone Agent 0.99.371.
- Self-hosters: the Convex schema gains tables for control grants, anonymous auth
attempts and browser sessions, and drops five columns from the drone status
table; schema changes are additive with no migration. The broker password
generator now reads its device and operator entries from a new
admin-authenticated route and writes the password and ACL files in place rather
than renaming them, because a file-level bind mount keeps the old inode through
a rename and the sync would report success while changing nothing the broker
could see. It needs its relay secret set, and per-deployment path overrides
where the broker container is not at the default path. Agents must send a
non-empty key when registering and the device's own key on the pairing-status
route. A push to the main branch does not rebuild anything: rebuild and
redeploy explicitly before relying on the control-grant path. - On macOS, and on Linux started outside an AppImage, the desktop app reports a
newer version and points at the releases page instead of installing it.
Windows and AppImage Linux still self-update. - The typecheck gate now covers the Convex backend, the desktop wrapper and the
four tools packages in addition to the app; CI lints with a warning ceiling
rather than blind to warnings, builds the production bundle on every push
instead of only on a release tag, and asserts tree, commit and backend
authorization invariants with guards that self-test before reporting clean.
Downloads
- macOS:
.dmg(Intel and Apple Silicon) - Windows:
.exeinstaller - Linux:
.AppImage
For the latest web version, visit command.altnautica.com.