Skip to content

Static download page -- license-as-credential build retrieval #2

Description

@darianmiller

The paid-customer download endpoint is built and closed (radSoftwareLicense #61): POST /api/downloads takes a .license file, verifies it with the same verifier the products ship, and returns the archived builds that license covers as short-lived, read-only, single-blob SAS URLs. Nothing consumes it yet.

This ticket is the page. It was scoped OUT of #61 on purpose -- "the static download page lives in the website repo" -- so it needs to exist here or it is a silent gap between a finished endpoint and a customer who cannot reach it.

Read radSoftwareLicense/docs/download-endpoint.md before starting. It is the contract: request shape, response shape, every status code, and the wording rules. The notes below are the parts that bite.

Scope:

  • A page on radprogrammer.com with a paste box and a file picker for the .license file, a fetch to the endpoint, and a rendered list of builds. No accounts, no passwords, no sessions -- the license IS the login. Stateless.
  • POST the license text as the RAW request body with Content-Type: text/plain (or application/json). Do NOT send it as form data: a form-encoded body is consumed into the server's parameter list, and although the endpoint now falls back to the raw parameter text, a multi-line .license FILE would be percent-encoded and mangled. Raw body is the supported path.
  • Render the two row states from the response's status field:
    • entitled -- a download link (the row's url).
    • not-in-maintenance -- shown, greyed, NOT a link, with the row's note text.
  • WORDING IS LOCKED. Never say "locked", "expired", "revoked" or "no longer available" for a build past maintained-through. Use the response's own note: "not included in your maintenance - renew to download". No purchased and properly licensed binary is ever locked -- the perpetual path reads no wall clock, so every build dated on or before maint verifies forever however long maintenance has lapsed. The maint date governs access to NEW builds only. A post-lapse build is one the customer never bought, not a capability withdrawn; what renewing unlocks is the DOWNLOAD, not the ability to run. Getting this wrong turns a renewal prompt into a false accusation that we broke their software.
  • Default to the recommended array, not to the newest row. recommended is the newest ENTITLED build per component and IDE major -- i.e. the newest build that is certain to RUN. Leading with the newest overall would offer a build that is greyed out.
  • Show version, buildDate, sizeBytes and sha256 per row so a customer can verify what they downloaded.
  • Handle every non-200 the endpoint documents, with a human message each: 400 (no license sent), 401 (invalid-license -- "we could not read that as a licence file"), 429 (rate limited; honour Retry-After), 503 (downloads-not-configured / downloads-unavailable -- an outage on our side, NOT "you own nothing"). Conflating 503 with an empty list is the failure that would tell a paying customer they have no builds.
  • A 200 with an empty builds array is a real answer and needs its own copy (e.g. an expired trial, which is entitled to nothing).

Security and privacy -- the license file is a CREDENTIAL and carries the buyer's email:

  • Never put it in a URL, a query string or a fragment; never log it; never persist it to localStorage, sessionStorage, a cookie or any analytics call.
  • Keep it in memory for the duration of the request and drop it.
  • The SAS URLs are short-lived (about 15 minutes) and single-blob. Do not cache them, and do not present a stale one -- re-post the license instead. A link that 403s because it expired should be re-fetched, not shown as a failure.
  • The page must be served from the exact origin the endpoint allows (RADLICENSE_DOWNLOADS_ORIGIN, default https://radprogrammer.com). CORS there is a one-entry allow list and never *, so a page on any other host -- including a www/apex mismatch or a preview deployment -- will be blocked by the browser. Confirm the origin before wiring it up.

Notes:

  • The public releases page keeps carrying only the current Community build. This page is the only route to the archive.
  • Depends on radSoftwareLicense #78 (provision the private container, wire CI publishing) for there to be anything to download. The page can be built and tested against a locally-run radLicenseServer before that lands.

Acceptance criteria:

  • A customer can paste or upload a .license file and see the builds it covers, with working download links for entitled rows.
  • Rows past maintained-through are shown, not hidden, are not links, and use the endpoint's own note wording; the page never describes a build as locked.
  • The default/primary offer comes from recommended, so the highlighted build is always one that will run.
  • 400, 401, 429 (honouring Retry-After), 503 and an empty-but-valid 200 each render a distinct, human message.
  • The license is never written to a URL, storage, a cookie, a log or an analytics call.
  • The page is served from the origin the endpoint's CORS allow list names, verified end to end against a running server.
  • Works without JavaScript frameworks and is usable with a keyboard and a screen reader.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions