RISE Technologies — RT monogram
RISE REDUCE
RISE Technologies — RT monogram
Part of this document is published fact; the rest is not written yet. 3 of 7 sections are statements of fact about the software, checked against the code on 2026-09-24. The other 4 are legally operative and await counsel. No agreement is formed by reading this page, and no section marked as awaiting counsel is a term, a representation, or an offer.

RISE Reduce — Privacy Notice

Version 0.7.0-disclosure · effective not yet in force · 3 of 7 sections written · statements of fact last checked 2026-09-24
SHA-256 of this document's canonical text: 4c6653c60297eca21933ef512dd32c5a010ea2e368931a9542647c30da75f47a
canonical text the hash covers

1. Who we are

AWAITING COUNSEL · legally operative, not drafted

[AWAITING COUNSEL]

2. What the software sends us

STATEMENT OF FACT · verifiable against the shipped code

The installed software sends RISE HQ metering records, and most of this section is what those records carry. Three wires carry one: the RMW1 binary wire, the v0 JSON wire, and — on the Air-Gapped plan, moved by the customer rather than sent — a sealed count record (src/wire/rmw1.ts, src/wire/ping.ts, src/wire/risecount.ts). A count record is recorded only when it is authenticated from a machine its licence is, or was, bound to (src/airgap/count.ts machinesThisLicenceWasBoundTo).

Metering records are not the only requests HQ accepts from an installation. POST /activate receives the licence id, the activation code, the device fingerprint and, when supplied, a client public key and a free-text reason for moving the licence to another machine, and its answer is written to be shown by the appliance (src/http/activateRoute.ts). POST /training/submit, POST /training/seed and POST /training/pack are closed: since RISE withdrew training on 2026-09-24, each answers 410 with the reason training_retired before any part of the request is read, and HQ receives, stores and sends nothing through them (src/http/trainingRoute.ts, src/http/trainingSeedRoute.ts, src/http/trainingPackRoute.ts). Until then, /training/seed and /training/pack answered a request naming the licence, its epoch and a data type, with an authentication code, with a sealed training seed or pack, and /training/submit took a sealed shape catalogue from a licence an operator had opted in (TrainingOptIn, recorded with the version and digest of the consent text shown, src/training/consentText.ts). A catalogue HQ received in that period is kept as it was received. A catalogue's accepted lines are shape patterns (a support band k20, k100 or k1000, the separator runs, and a token class per slot from a closed list, a whole number carrying a width band), column lines (an ordinal, a cardinality band and a sortedness word), dependency lines (two ordinals) and one file line (charset, line ending and empty-value spelling, each from a closed list), or, on format version 4, word-count bands only; a catalogue with any other line is refused (src/training/shapeArtifact.ts checkArtifact; measured 2026-09-15 with added scorecard, coverage, reading, filename and row-count lines, each refused). POST /enroll/:licenseId receives the licence's X25519 public key and an opaque enrollment blob holding the private key wrapped under the customer's passphrase and recovery phrase with their salts and KDF identifier, and stores both, write-once, on the licence (License.unlockPubKey and unlockEnrollment, src/http/enrollRoute.ts). Grant and lease files are downloaded from GET routes (src/http/grantRoutes.ts, src/http/leaseRoutes.ts). Which of these requests a shipped engine makes itself, rather than a person or a script on the customer's side, is engine behaviour and is not established by this document.

Everything the metering wires declare, in full. Identifiers: the licence (on the binary wire, a 16-byte tag derived from the licence id), the customer (on the JSON wires), and a device fingerprint. Timing: the start and end of the measured window, an epoch, a sequence number, and on the binary wire a cross-boot counter. Quantities: metered GB-months for the window and cumulatively, total logical bytes across the estate, total stored bytes, the device's own signed saved-bytes figure, and on the current binary ping the lifetime integrals of logical and of stored bytes over time in GiB-seconds, which are HQ's own billing input. Deployment shape: the mount mode (transparent projection or watched folder) and four capability flags — virtual placeholders, hydrate-on-read, requires-admin, materialize-on-open. Group: on the binary PING3 and COUNT3 records, a 16-byte customer-group tag derived from HQ's group identifier. The Air-Gapped count record adds the month it covers, the device's claimed savings figure and claimed share (display only; HQ recomputes and never bills from either), a container file identifier and a generation timestamp. Each record ends in the code that authenticates it.

Apart from the framing that tells HQ how to read a record (magic, wire version, record type, body length and zeroed reserved bytes), that list is everything a metering record declares. No file name, no file path, no folder name, no per-folder figure, no per-file figure and no hash of customer data is a field of any of the three. Each binary record type accepts a fixed set of body lengths (one length, except the ping, which accepts 64 or 72 bytes: src/wire/rmw1.ts ACCEPTED_PING_BODY_LENS) and refuses non-zero reserved bytes and trailing bytes. The JSON ping schema is strict rather than stripping, so a device that adds a field to a ping is refused by name. The JSON count schema is not strict: a field it does not declare is dropped before the count's authentication code is checked, so the code still verifies, and HQ's stored copy of that accepted count keeps the uploaded body as it was parsed from the file, re-serialised, the undeclared field included (src/airgap/count.ts, src/airgap/countJudge.ts). The per-folder identifiers and counts went out of the database on 2026-08-01 (migration 20260801000000_drop_folder_identifiers); the per-folder byte sums came off the binary wire on 2026-08-02 and the JSON wire on 2026-08-03; and no code path in HQ writes a per-folder row today (src/metering/intake.ts).

Customer file content is never transmitted. Reduction, restoration and encryption run on hardware the customer controls. HQ recomputes the billed quantity from the totals in the record; the device's own saved-bytes figure is evidence used to refuse an appliance whose arithmetic disagrees, and is never itself the billing input.

No wire HQ accepts carries a customer passphrase, a data key or a recovery phrase, and the database has no column for one. The key material that travels with a grant is the non-secret wrapped blob and its derivation parameters; what opens it exists only on the customer's own machine (src/wire/grantBody.ts).

3. What we collect when you buy

STATEMENT OF FACT · verifiable against the shipped code

A purchase starts on HQ's own order page and completes on payment pages Stripe hosts. The order page records, in SignupOrder: the buyer's name; the buyer's work e-mail address, in full and lower-cased; the organisation name; whether the licence is for the buyer's organisation or another one and, for another one, its name and a contact's e-mail address, in full; the Air-Gapped expected-data range; the plan, term and API add-on chosen (the record's training-types field is always empty: since RISE withdrew training on 2026-09-24 the order page offers none and checkout refuses an order that names one); and the Terms acceptance, which is the time and the version and SHA-256 of the terms document (src/http/checkout.ts). Every e-mail address on an order is confirmed with a 6-digit code: CheckoutEmailVerification keeps a domain-separated SHA-256 of the address and a scrypt verifier of the code, while the outbound e-mail copy (EmailOutbox) keeps the recipient address and the message text, the code included, as it does for every e-mail HQ sends, activation codes included. After Stripe's setup page saves a card or US bank account, and before anything is charged, HQ reads that payment method's fingerprint and keeps a domain-separated SHA-256 of it on the order (cardFingerprintHash), with the Stripe customer id and the result of the card check. HQ sends Stripe the confirmed e-mail address (on the setup page), the order id, the plan choice and the version and SHA-256 of the terms and privacy documents (session metadata). The order page asks for no phone number, job title, company size, seat count, marketing preference, date of birth or identity document. MEASURED 2026-09-15 on HQ's real routes with the in-memory store and the repo's Stripe test double. WHAT THAT MEASUREMENT DOES NOT COVER, said here rather than left for a reader to assume: it was taken against the IN-MEMORY store only. The same routes write through a Prisma store in a deployment, and the two runs that would show what that store actually keeps — test/prisma.spec.ts and test/trainingPrismaStore.spec.ts — were skipped on 2026-09-15 and again earlier on 2026-09-20 because localhost:5432 was closed. THEY RAN ON 2026-09-20 IN ROUND 6: localhost:5432 was open, and both were executed against the dedicated test database on real Postgres (test/prisma.spec.ts 65 of 65 and test/trainingPrismaStore.spec.ts 34 of 34, all passing). What those runs prove is the Prisma store's CONTRACT. They were not walked field by field against the list above, and no lane has done that, so this section still states what HQ's code writes rather than a column-by-column verification of where it lands.

From Stripe's events HQ records Stripe's own identifiers — customer, subscription, checkout session, invoice — and builds an invoice mirror from them which carries the organisation name and e-mail address Stripe reports for that invoice, the line amounts, the tax figures and the payment status. The licence, grant and metering rows a purchase mints are keyed by those identifiers, and the licence keeps the e-mail address its codes and notices go to (License.customerEmail): the buyer's, or the other organisation's contact's.

HQ also keeps a copy of every event Stripe delivers to it, including event types it does not act on, because the copy is written before the event is dispatched (WebhookEvent.payload, written by src/webhook/processor.ts), and no code deletes those copies. A copy keeps the event's own structure: the identifiers, amounts and metadata, the billing name, e-mail address, phone number and billing address the event carries, a card's brand, country and network, and a bank's name. Before a copy is stored, the value of every tax ID in a tax_ids or customer_tax_ids list is replaced with a redaction marker, and so, inside payment-method details and inside any object that is itself a payment method, card, bank account, source or mandate, are the fingerprint, the last digits (last4, dynamic_last4, iban_last4), the expiry, every bank routing, sort, bank, branch, institution, transit and account number, the bank identifier code, the verified account-holder name, e-mail address and phone number, a PayPal payer's e-mail, id and name, a boleto payer's tax number and the generated SEPA debit references (redactStripePayloadForStorage, src/money/accountMatch.ts). MEASURED 2026-09-15 with that function on Stripe-shaped card, US bank account, iDEAL, Bancontact, SOFORT, EPS, giropay, P24, PayPal, boleto and SEPA debit charges, a top-level payment method, a customer and an invoice: none of those values survived; a P24 reference and a SEPA mandate id did. HQ's own checkout and Reserve sessions offer card and us_bank_account only (src/http/checkout.ts, src/http/reserveRoutes.ts). Copies stored before a key was added to the redaction (the first version on 2026-09-14, the wider list afterwards) are not rewritten.

For the one-organisation and one-licence-per-machine rules HQ keeps: for each tax ID a checkout carries and each payment-method fingerprint a charge carries, a domain-separated SHA-256 of the normalised value, with the last four characters of a tax ID as a display hint (AccountSignal); for a company e-mail domain, recorded only after a code sent to an address at that domain has been typed back (the 6-digit checkout code at order completion or at an organisation contact's confirmation, or the activation code at /activate), the EXACT domain of that address — never a shortened form of it, and never a registration suffix such as co.uk or uk.com, which HQ does not record at all (src/money/accountMatch.ts verifiedDomainKeyOf); the device fingerprint each licence is activated on (License.deviceFp) and a record of each licence bound to, released from or refused on each fingerprint; each purchase held for review, with the accounts and signal kinds it matched and the named operator's decision and basis (AccountMatchReview); each operator override of the one-licence-per-machine refusal (MachineLicenceOverride); and, where an operator releases a purchase as a separate organisation, a payer_organisation_link fact per matched payment fingerprint (src/money/accountMatch.ts recordPayerOrganisationLinks). A tax ID is short, so its hash is a matching key and not a secrecy measure. A later buyer is matched to an account only on a name somebody PROVED: their own e-mail domain, a domain theirs is a name under, or a name under theirs (src/money/accountMatch.ts organisationDomainMatch, round 6 of 2026-09-20; until then the look-up walked parents only). A name that two or more DIFFERENT accounts have proved names beneath — how a registry's or a hosting provider's domain looks — is treated as shared and is matched in NEITHER direction (hostIsSharedName, SHARED_NAME_MIN_ACCOUNTS 2). WHAT THE BUYER'S OWN DOMAIN IS AND IS NOT EXEMPT FROM, corrected in round 6 of 2026-09-20 after a review measured it (this fact and the website's twin both stated flatly that the buyer's own exact domain was exempt from that test): an account recorded under the buyer's EXACT domain is matched whether or not that domain is shared, because it is the name the buyer proved; but the look-up DOWNWARD from the buyer's own domain is subject to the test, so once two or more different accounts have proved names beneath it, no account recorded beneath it is matched. MEASURED on the real organisationDomainMatch on MemoryStore (scratch billing3/terms/r6f/probes/p2_match.mts): cus_apex proving mycorp.example with cus_t1 and cus_t2 beneath gives a buyer at mycorp.example matchedCustomerIds ['cus_apex'] and sharedNamesDropped ['mycorp.example']; with only cus_t1 beneath, the same buyer gives ['cus_apex','cus_t1'] and drops nothing. Two organisations that merely share a registration suffix are two organisations. Which hosts count as registration suffixes is a list HQ maintains (MULTI_LABEL_PUBLIC_SUFFIXES plus the state-level .us tree); a two-label host that is not on it is recorded as a company domain, gen.io included, which round 5 did not do. A licence bought for ANOTHER organisation records no account identity for that organisation: that contact's confirmed domain is not written on the payer's account at checkout or at activation, so that organisation's own first purchase is an ordinary new account (src/http/checkout.ts, src/http/activateRoute.ts). Before payment HQ answers a buyer only on the company domain of the e-mail address the buyer confirmed and on the card the buyer saved, and it looks up no tax ID and no machine (src/money/accountMatch.ts verifiedEmailUpgradeMatch, src/http/checkout.ts cardMatch).

THOSE MATCHING ROWS CAN MOVE FROM ONE CUSTOMER'S ACCOUNT TO ANOTHER'S, and this section did not say so until 2026-09-20. When an operator at /console/accounts decides a purchase made for the buyer's OWN organisation is a duplicate and moves it onto an existing account, and the move leaves the purchase's account holding no other licence, every AccountSignal row of that emptied account — its verified company e-mail domain, its tax-ID hashes and its payment-fingerprint hashes — is re-homed onto the account the purchase was moved onto, inside the move's transaction, and an account_signals_rehomed fact records the operator, the emptied account and how many rows moved (src/http/console/accounts.ts rehomeOrganisationSignalsAfterMove, HqStore.moveAccountSignals; src/db/store.ts, src/db/memory.ts, src/db/prisma.ts). A row whose kind and hash the target already holds is deleted from the emptied account rather than moved. Nothing is re-homed when the emptied account still holds another licence, and nothing is re-homed for a purchase made for another organisation, whose account is the payer's.

Where an operator records a tax exemption, HQ keeps the certificate or registration number it rests on in full (CustomerTaxExemption.certificateRef; for reverse charge, the customer's VAT number), prints it on the invoice (src/documents/invoiceDoc.ts) and sends it to Stripe as customer metadata (src/stripe/customerTaxExempt.ts).

HQ writes a ConsentRecord (src/db/store.ts) for each completed sale from its order's Terms acceptance, and for a Stripe session whose consent reports the terms of service accepted: the document kind, its version, the digest of the exact text and the time. The row has columns for an IP address and a user-agent string; both writers leave them empty (src/webhook/processor.ts applySignupOrderAtCompletion and recordCheckoutConsent; measured 2026-09-15: ip null, userAgent null).

Separately, HQ's request log records the IP address of every request it receives, metering records and activations included (the request serializer in src/http/server.ts). Activation, metering, checkout (including the e-mail code requests), download-link and Air-Gapped upload requests are rate-limited per IP address where HQ sees the client's address. The three closed training routes are not rate-limited: each answers 410 before reading the request (src/http/trainingRoute.ts). THE E-MAIL CODE HAS A SECOND BOUND, added to this section 2026-09-20: HQ counts, per registrable COMPANY e-mail domain and per hour, the codes it sent and the wrong codes it was given, once for each client at that domain (30 sends, 15 wrong) and once for all clients at it together (300 sends, 150 wrong), and the all-clients bound never refuses a client that has not itself reached 3 sends or 3 wrong codes at that domain in the hour (src/http/checkout.ts EMAIL_CODE_DOMAIN_LIMIT). The client is req.ip. A THIRD BOUND SITS IN FRONT OF BOTH AND THIS SECTION DID NOT NAME IT UNTIL ROUND 6 OF 2026-09-20: a WHOLE-PROCESS window of 120 code requests and 300 code checks a minute (EMAIL_CODE_SEND_LIMIT.globalPerMinute, EMAIL_CODE_CONFIRM_LIMIT.globalPerMinute), incremented and tested BEFORE the per-domain bounds are consulted (admitEmailCodeSend at :892 before domainSendAllowed at :900; admitEmailCodeConfirm at :968 before domainGuessesLeft at :980), so a caller that has asked for nothing IS refused once the process has answered 120 requests in that minute — MEASURED on the real route: 120 requests from 120 addresses at 120 unrelated domains all answered 202, and the next request from an untouched address at an untouched domain answered 429. That refusal names no address and no account. These counters are in memory, per process, and a restart clears them; the per-address bound (five guesses a code, three codes an hour) is durable. WHICH BOUND SKIPS A PERSONAL ADDRESS, corrected in round 6 of 2026-09-20 after a review found the sentence had drifted away from its antecedent: the PER-DOMAIN bounds skip it, and the whole-process bound does not. A personal e-mail domain, which includes a mail relay or alias service, has no per-domain budget at all (companyDomainKey returns null, so domainSendAllowed admits it); the whole-process window counts every request, personal addresses included. MEASURED 2026-09-20 on the real route (scratch billing3/terms/r6f/probes/p3_personal.mts, buildServer on MemoryStore): 120 requests at gmail.com, outlook.com and duck.com from 120 different addresses all answered 202, an untouched buyer at an untouched COMPANY domain then answered 429, and the same buyer with the window clear answered 202; 350 per-domain sends at gmail.com from ONE caller were all allowed where the same caller at a company domain was refused at request 30. An organisation contact confirming a paid order is bounded per ORDER instead (five guesses a code, three codes an order an hour, ten tries in ten minutes). Refused activations and refused metering records log the address again (src/http/activateRoute.ts, src/http/meterRoute.ts). Where that log is kept, and for how long, is set by the deployment, not by HQ's code. A request for a download link keeps the e-mail address it names (DownloadToken.email, src/http/downloadRoutes.ts), and a licence enrolled at POST /enroll keeps the key material section 2 describes.

The schema holds no table of file names. It keeps one per-folder table, FolderSnapshot (prisma/schema.prisma), for rows written before the per-folder figures came off the wires: each row holds byte counts and the licence, customer and device keys, with no folder name, path or identifier, and no code path writes to it today (section 2). It has no column anywhere for a card number, an expiry date or the last four digits.

4. Payment data

STATEMENT OF FACT · verifiable against the shipped code

Card and bank account numbers never reach a RISE server. Stripe, Inc. hosts the payment page and is the money-of-record. The events Stripe sends back to HQ carry Stripe's identifiers, the amounts, the status and the billing details on the purchase; a charge event also carries the payment method's brand, country, last digits, expiry and Stripe's fingerprint for it. Before any charge, HQ also reads the fingerprint of the card or bank account Stripe's setup page saved, and keeps it only as a hash. Section 3 states which of those HQ stores.

The invoice renderer accepts an optional card brand and last-four for display, in the way a receipt normally shows one. No code path in HQ supplies those values today and the database has no column to keep them in, so a rendered RISE invoice shows no card detail at all.

5. Retention

AWAITING COUNSEL · legally operative, not drafted

[AWAITING COUNSEL]

6. Your rights

AWAITING COUNSEL · legally operative, not drafted

[AWAITING COUNSEL]

7. Contact

AWAITING COUNSEL · legally operative, not drafted

[AWAITING COUNSEL]

PRODUCT FACTS SUPPLIED TO COUNSEL — NOT TERMS