Commit Graph

3 Commits

Author SHA1 Message Date
Keysat 8a519ee25d Fix Settings modal crash: send licenseId as string, not Uint8Array
The Keysat client's payload exposes both:
  • licenseId    — raw 16-byte UUID as Uint8Array
  • licenseUuid  — same value, canonical string form

server/license.js was sending licenseId (the Uint8Array). After JSON
serialization that turned into an object like {"0":1,"1":2,...} on
the wire. The frontend's renderLicenseBlock() calls
`lic.licenseId.slice(0, 8)` to abbreviate the ID for display — .slice
doesn't exist on that object, so the template threw, the
app.innerHTML assignment silently aborted, and clicking the gear
looked like a no-op.

The render error guard added in 0.1.17 caught it on first repro:
  Render error: lic.licenseId.slice is not a function

Fix: switch to payload.licenseUuid (the string form).
2026-05-08 13:00:29 -05:00
Keysat 2621f2cdbe Add online license revocation check (Keysat /v1/validate)
Without this, a license revoked in the Keysat admin UI keeps unlocking
the app on the customer's machine — Ed25519 signatures are perpetually
valid, so the offline-only check never sees the revocation.

What this adds:

  • license.js: validateOnline() calls licensing.keysat.xyz/v1/validate
    via @keysat/licensing-client's Client. Hard rejections (revoked,
    suspended, expired, not_found, product_mismatch, fingerprint_mismatch,
    too_many_machines, invalid_state) immediately flip state to "invalid"
    and persist the verdict to <license>.state.json so it survives
    restarts. rate_limited and unknown reasons are treated as transient.

  • Network errors keep the prior state for up to MAX_OFFLINE_DAYS
    (default 7, env-overridable) since the last successful validate.
    Past the ceiling, lock out with reason=validation_overdue. This
    avoids breaking customers when Keysat is briefly down while still
    catching revocations on machines that go offline forever.

  • license.js: deactivate() helper that removes both license.txt and
    its sidecar state file (idempotent). publicView() now exposes
    lastValidatedAt, serverStatus, graceUntil for the UI.

  • index.js: refreshLicenseOnline() runs on startup (async, non-
    blocking), every 6h thereafter (env-overridable), and at activation
    time with an 8s timeout cap so a slow Keysat doesn't hang the
    activation UI. State changes are logged.

  • index.js: /api/license/activate now awaits an online confirmation
    after the offline signature check passes. A revoked key pasted into
    the activation modal fails fast instead of working until the next
    poll.
2026-05-08 10:39:11 -05:00
Keysat 574a16d9fa Save in-progress keysat integration and StartOS 0.4 work
Snapshot of the working tree before cleanup. Captures:
- Keysat licensing: server/license.js, /api/license/* endpoints in
  server/index.js, activation modal in public/index.html, embedded
  Ed25519 issuer key (assets/issuer.pub).
- StartOS 0.4 expansion: setApiKey action, version files v0.1.1
  through v0.1.15, file-models/config.json.ts, manifest updates.
- Self-hosted registry server (startos-registry/).
- Build/deploy scripts (bin/bump-version.sh, bin/deploy.sh, vendored
  yt-dlp binary), .gitignore, .deploy.env.example.
- Recent design docs (KEYSAT_INTEGRATION.md, UPGRADE-DESIGN.md) —
  retained here so they remain recoverable when removed in the
  follow-up cleanup commit.
2026-05-08 09:39:17 -05:00