# drift-gateway-helper 0.6.20 — 2026-10-13

Linux (Debian 10 / glibc 2.28) build added. Same hardened source as the
Windows build (`helper-protect` + `enc!/d!` string obfuscation + RSA
`license.key` gate), only the linker glibc baseline is lower.

Previous reference binary: 0.6.10 (2026-10-01). Everything below has
accumulated across 0.6.11…0.6.20 and is now shipping as a single
cross-platform drop.

## 🆕 1. Linux (Debian 10) binary — new platform target

```
dist-debian10/drift-gateway-helper           ← ELF, GLIBC_MAX ≤ 2.28
```

Loads on **Astra SE 1.7/1.8, Debian 10/11/12, RHEL/CentOS 8+,
Ubuntu 20.04+** — one binary covers every supported Linux target,
including the hardened government distros that pin glibc 2.28.

Same source as the Windows helper, same hardening layer, same
`license.key` format.

## 🆕 2. Delegate-resolution for event polls (0.6.19)

A per-poll **8-character resolve code** (format `AAAA-AAAA`) can
now be handed to a trusted secretary / assistant, allowing them to
**fix the final slot** of an event poll without full account access.

- The code is minted client-side at poll creation and stored next to
  the poll alongside the existing access password.
- The results page gets a 🔒 button on each slot: clicking it opens
  a focused dialog (just that slot's code / name / optional email),
  no extra side-panel.
- On submit, the gateway's new `/poll-resolve/<access-pwd>` endpoint
  verifies the resolve code (constant-time compare), writes
  `status: "resolved"`, `resolvedSlotId`, `resolvedBy`, `resolvedAt`,
  and fires a notification push to the organizer.
- The organizer's calendar + Feedback dashboard update **live** — no
  reload needed — via `notify_feedback_changed`.

Use case: demoing drift to a company where you don't know everyone
up-front — send the vote link to a contact person at the company,
hand them the resolve code separately, and let them pick the final
slot once they've chased everyone to vote.

## 🆕 3. Shareable results-only URL (0.6.12)

Each poll now has a public **results view** at `/poll-results/<id>` —
same URL shape as `/poll/<id>` but read-only. Share it with the whole
invitee list once a poll is live; participants can watch the grid
fill up without being able to re-cast anyone else's vote. Lives
behind the same access-password gate as the vote URL.

## 🆕 4. Vote-recovery for self-join (0.6.11)

Previously, a vote from an email address not already in the poll's
`participants[]` was silently discarded (the organizer still got the
"voted" notification, so the loss was invisible). The vote URL already
proves email ownership via the per-poll `accessPwd`, so the gateway now
**inserts the missing participant** on first vote rather than drop it:

- New row carries `recoverInserted: true` so drift's UI can flag it
  for the organizer ("recovered — check name / type").
- Backed by a complementary read-merge-write in drift's `savePoll`
  (ships as drift-offline 4.0.47+): any gateway writes (joins, votes)
  that happen between the organizer's last read and their next save
  are preserved rather than clobbered by a stale snapshot.
- No spoofing surface: everything above the array scan still requires
  the per-poll password.

## 🆕 5. Live push on vote / join (0.6.10 → tightened in 0.6.11)

`submit_vote` and `submit_join` fire `notify_feedback_changed` through
the notification helper. Combined with the drift-offline
`useFeedbackLiveUpdates` composable, the organizer's calendar poll
overview, mail-view poll block, and Feedback dashboard **all refresh
in real time** — no reload, no module switch.

## 🔒 Hardening — same as the other helpers (0.6.19)

`main.rs` calls `helper_protect::Protection::init(...)` at startup
and refuses to run if:

- the binary has been tampered with (self-SHA256 verification),
- the running host is past the demo cutoff **and** no valid
  `license.key` is on disk,
- the `license.key`'s licensed domain doesn't match the installed
  **CGP main domain**,
- the licence is past its own expiry.

Compile-time string obfuscation (`enc!` / `d!` from
`string-encrypt`, keyed off `SHA256(pubkey)`) means every URL, SQL
fragment, CGP command name, and WSSP route string in the binary is
XOR-encoded at rest.

Startup logs confirm the gate armed:

```
[PROTECT] ok (hardened build)
```

Startup logs on a bad / missing licence:

```
[PROTECT] refusing to start [<reason>] — install a valid license.key / restore the original binary
```

The watchdog **polls `license.key`'s mtime every 60 s** — push a
renewed key and the running helper swaps the in-memory state atomically,
no restart required. Invalid replacement files log the error and keep
the current licence active (operator typo cannot drop the deployment
into Demo mode).

## 🔧 Infrastructure — port-hold-fix (0.6.14)

`socket2 = "0.5"` added as a cross-platform dep. The listen socket on
`127.0.0.1:9084` now sets `SO_REUSEADDR` and (on Windows) clears
`HANDLE_FLAG_INHERIT` **before** bind, which is the root fix for the
6-day CGP port-hold outage (2026-09-23 – 2026-09-28) where every
restart failed with `os error 10048` because a CGP-inherited socket
handle survived helper death. Linux also benefits — `SO_REUSEADDR`
clears orphan-bind on restart there too.

## Compatibility

- **Windows**: `drift-gateway-helper.exe` — Windows 10 1809+ (same
  as before).
- **Linux**: `drift-gateway-helper` — GLIBC ≤ 2.28 (Astra SE 1.7/1.8,
  Debian 10/11/12, RHEL/CentOS 8+, Ubuntu 20.04+).

Drop-in replacement for 0.6.10: same `config.toml`, same
`driftgw.wcgp` forwarder, same WSSP deployment. Everything new is
either opt-in (resolve code is minted on first-save of a poll, no
work on old polls) or server-driven (hardening gate / live push).

## Where to pick up each binary

| Platform | Download URL |
|---|---|
| Windows | `/downloads/win/drift-gateway-helper.exe` |
| Linux (Debian 10+) | `/downloads/linux/drift-gateway-helper` |
| Setup Guide | `/downloads/drift-gateway-helper/SETUP.md` |
| WSSP Deployment | `/downloads/drift-gateway-helper/CGP-WSSP-DEPLOYMENT.md` |
