RISE Reduce — Privacy Notice kind: privacy version: 0.7.0-disclosure effective: not yet in force status: disclosure sections: 7 (4 awaiting counsel) statements of fact last checked against the code: 2026-09-24 1. Who we are [LEGALLY OPERATIVE — RISE does not draft this text.] [AWAITING COUNSEL] 2. What the software sends us [STATEMENT OF FACT — authored by RISE engineering, verifiable against the shipped code. Not a term.] 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 — authored by RISE engineering, verifiable against the shipped code. Not a term.] 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 — authored by RISE engineering, verifiable against the shipped code. Not a term.] 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 [LEGALLY OPERATIVE — RISE does not draft this text.] [AWAITING COUNSEL] 6. Your rights [LEGALLY OPERATIVE — RISE does not draft this text.] [AWAITING COUNSEL] 7. Contact [LEGALLY OPERATIVE — RISE does not draft this text.] [AWAITING COUNSEL] --- PRODUCT FACTS SUPPLIED TO COUNSEL (NOT TERMS, NOT BINDING) --- - Encryption label is "AES-256-GCM + RCC". No larger AES key-size label has ever been true of this product and none may appear in the drafted text — src/lib/claimsLint.ts fails any customer surface that carries one, including this document. - NOT PUBLISHED, AND WHY: the metering seed's seal mechanism. HQ seals the seed to a public key and only the matching private key opens it, and that private key is not held at HQ (src/grants/hpkeSeal.ts, test/hpkeSeal.spec.ts). WHICH PARTY GENERATES THE KEY PAIR was not established by reading the code in the time available, and the enrolment route that would settle it has never minted a token in this deployment. So the mechanism stays a note rather than a disclosure: section 2 publishes only the part that is checkable today — that no wire and no column carries the secret. Establish the generation side before any drafted text describes it. - RETENTION, AS IMPLEMENTED (the constraint on section 5): HQ has exactly one deletion policy and it covers exactly one thing — the stored payload bytes of an accepted metering record, purgeable after ACCEPTED_RECORD_RETENTION_DAYS = 2,557 days (seven years). Even then the ROW survives: only the payload is emptied and a tombstone stamped, so the digest, the byte length and the booking pointer remain permanently (src/db/store.ts). No other table has a deletion policy, the purge sweep is not wired to any schedule, and no customer-initiated erasure path exists anywhere in HQ. That includes the stored copy of every Stripe event (WebhookEvent.payload), the account-matching tables added on 2026-09-14 (AccountSignal, AccountMatchReview, MachineLicenceOverride), and the tables billing pass 3 added (SignupOrder, CheckoutEmailVerification, AirgapPoolClose), the copy of every e-mail HQ sent with its codes (EmailOutbox), download-request addresses (DownloadToken) and accepted training catalogues (TrainingArtifact). A drafted retention period shorter than that describes software that has to be built first. - CONTACT (the constraint on section 7): the support address on statutory documents is a literal in docLegal() (src/http/documentRoutes.ts); only the postal address and the EIN come from environment configuration. This fact said until 2026-09-14 that the address came from environment configuration, which a review found false. It is still not written into this document, because a value that can differ by deployment would make a consent record uncheckable. Counsel should name the address in the delivered text. - The three sections above are published FACT, not draft: they are what the code does, they are digested with the rest of the document, and they must not be contradicted by the sections still awaited. - ACCOUNT-MATCHING DATA (founder rulings 2026-09-13/14; the constraint on sections 5 and 6). The one-organisation rule matches accounts on three recorded signals and three only: a verified company email domain, a tax ID and the payment-method fingerprint the payment processor assigns (src/db/store.ts ACCOUNT_SIGNAL_KINDS = email_domain, tax_id, payment_fingerprint; that file's own note reads THE MACHINE IS NOT A ROW HERE). For a personal email domain the domain is not recorded at all, so such an account matches on the last two. THE MACHINE IS A SEPARATE RULE, NOT A MATCHING SIGNAL: one active licence per machine is enforced at activation and at a move, where it REFUSES with a named reason and an operator override (founder ruling 10d; src/http/activateRoute.ts bindUnderMachineLock raises no account match), and 'machine' appears only as something an operator's review record can name. THIS PARAGRAPH SAID UNTIL 2026-09-20 that the machine was a fourth matching signal and that a personal-email account matched 'on the last three only'; both were false against the code and against the terms fact in this same file, and a review found it after an earlier edit had touched the string without correcting them. BUILT 2026-09-14 (see section 3 for what is stored). THIS FACT SAID ON 2026-09-13 THAT THE SCHEMA HAS NO COLUMN FOR A TAX ID AND THAT NOTHING IN HQ READS A PAYMENT FINGERPRINT. Both were wrong or have stopped being true: CustomerTaxExemption.certificateRef holds the certificate or registration number an operator records for an exemption (for reverse charge, the customer's VAT number), which src/documents/invoiceDoc.ts prints and src/stripe/customerTaxExempt.ts sends to Stripe; every Stripe event's data object was written verbatim to WebhookEvent.payload, including a checkout's tax ID values and a charge's card fingerprint, last four digits and expiry, until src/webhook/processor.ts began redacting before storage on 2026-09-14 (rows written earlier are not rewritten); and src/money/accountMatch.ts now reads tax IDs from checkout sessions and fingerprints from charges and stores their hashes. Since billing pass 3 the 6-digit checkout code also verifies a company domain, and a release as a separate organisation writes a payer link. The website's Privacy Policy (rise-legal.js, document set version 2026-09-24) states where each item is kept and lists exactly the fields the sign-up form takes. MATCHING DATA ALSO MOVES BETWEEN ACCOUNTS, added here 2026-09-20 in round 5b: when an operator moves a purchase made for the buyer's own organisation onto an existing account (ruling 10c) and the move leaves the purchase's account holding no other licence, every AccountSignal row of that emptied account is re-homed onto the account it was moved onto, inside the move's transaction (src/http/console/accounts.ts rehomeOrganisationSignalsAfterMove, HqStore.moveAccountSignals). A rights or retention section drafted on the assumption that an identifier stays with the customer it was recorded against would be wrong about this. A drafted rights section that describes the data held as billing identity and aggregate usage only would leave all of this out.