docs(legal): dedicated Privacy Notice separate from T&Cs 1.5 #35

Open
opened 2026-09-05 18:57:17 +00:00 by PlasticDigits · 3 comments

Summary

Publish a dedicated Privacy Notice as a first-party HTTPS document, separate from T&Cs Version 1.5, then link it from the four product footers.

T&Cs Version: 1.5 (TERMS_AND_CONDITIONS.txt) has no privacy / cookies / data-processing section. TermsGate, @cl8ytermsbot, the DEX indexer, WalletConnect / Reown, Coinbase Wallet SDK, request logs, and first-party agent-box tool connectors already process data. None of that is disclosed in a document a visitor can open from the footer.

This issue is the canonical notice + https://terms.cl8y.com/privacy + footer links. It does not absorb SPA legal-guess routing, storage-consent / idle SDK init, or TermsGate coverage parity. Those stay on the issues named below.

Bundle (do not split):

  1. Author and version a Privacy Notice in this repo, independent of the terms oracle / content_sha256 re-sign path.
  2. Serve it at compile-time https://terms.cl8y.com/privacy (portal route; optional read-only API content route that is not bound into acceptance messages).
  3. Footer links labeled Privacy (not Terms) on cl8y.com, dex.cl8y.com, bridge.cl8y.com, and terms.cl8y.com, all pointing at that same HTTPS URL.

Decisions (do not re-litigate unless product changes them):

Surface Decision
T&Cs 1.5 Unchanged. Do not fold privacy prose into TERMS_AND_CONDITIONS.txt.
Canonical URL https://terms.cl8y.com/privacy (exact origin + path, HTTPS, no query/hash).
Marketing cl8y.com Footer Privacy link only. Still no TermsGate (#34).
DEX / Bridge Footer Privacy link. Do not treat clickwrap or the DEX risk modal as this notice.
Legal portal /privacy must not fall through to T&Cs home (renderHome).
Counsel Legal copy is human-required before merge. Engineering supplies an accurate processing inventory; counsel owns wording, lawful-basis language, and retention periods.

Not a duplicate of:

  • CL8Y-web#12 — reserved-path routing (/privacy must not paint Swap / Transfer / homepage). That issue says 404 until a canonical notice exists; authoring the notice is out of scope there. After this URL exists, #12 may 301 reserved paths to this constant.
  • #34 — TermsGate coverage matrix. Out of scope there: “Authoring a Privacy Notice”.
  • cl8y-bridge-monorepo#165 — storage consent that stops idle WalletConnect pulse and Coinbase CCA. That issue must link this notice when it exists; it must not author policy prose.
  • cl8y-dex-terraclassic#1202 — privacy-reviewed product events. Events must stay consistent with this notice; this issue does not ship the event pipeline.

Current codebase

Piece Behavior today
T&Cs TERMS_AND_CONDITIONS.txt Version: 1.5, effective 10 August 2026. Privacy appears only as incidental “privacy laws” language. Hash-aware oracle in api/src/terms/sync.rs binds content_sha256 into wallet/Telegram acceptance messages (api/src/message.rs).
Portal https://terms.cl8y.com. web/src/main.ts routes `/sign/evm
API https://api.terms.cl8y.com. Terms routes: GET /api/v1/terms/latest, /terms/latest/content, /terms/{version_label}. Signature routes: GET /signatures/status, POST /signatures/wallet, POST /signatures/telegram. No privacy document route.
TermsGate SDK packages/cl8y-clickwrap — TermsGate polls property + network + account. Networks: EVM / TerraClassic / Solana / Telegram.
WalletConnect / Reown Legal-owned VITE_WC_PROJECT_ID (Reown Cloud) for /sign/evm and Galaxy Station WC v2 on /sign/terra-classic (web/src/evm/walletConnect.ts, web/src/terra/walletConnect.ts). No @reown/* direct import; no Coinbase Wallet SDK in this repo.
Request logs tower_http TraceLayer on the API. Client IP is used for in-memory rate limits only (api/src/rate_limit.rs: TCP peer by default; rightmost XFF hop only inside TRUSTED_PROXY_CIDRS). IPs are not columns on signatures / terms_versions / bot tables.

TermsGate / signature fields actually persisted (api/migrations/001_initial.sql, WalletSubmitBody / TelegramSubmitBody in api/src/routes/signatures.rs):

  • Wallet: property, network, account_id, message, signature, optional pubkey, client_timestamp, optional version_label; proof JSON { type, signature, pubkey }.
  • Telegram submit (verify-then-drop): widget id, first_name, last_name, username, photo_url, auth_date, hash, or Mini App init_data. Stored: account_id = Telegram user id, account_display = @username if present, proof { type, auth_date } or { type, hash, auth_date }. Names/photos are used for hash verify, then dropped (not columns).
  • Canonical message binding lines: Property, Network, Account, Content-SHA256, Accepted at (UTC) — T&Cs bytes only. Do not add Privacy Notice hash to this message.

Telegram enforcement bot (bot/, public README name @cl8ytermsbot):

  • bot_chat_state: chat_id, last_known_version, last_reminder_at
  • bot_member_compliance: chat_id, user_id, required_since
  • Numeric ids only in those tables. Fail-closed on Legal API errors (#5).

There is no PRIVACY_NOTICE* file and no privacy version table.

Marketing (code/CL8Y-web) — sibling PR

  • Routes in src/app/index.tsx: /, blog, legacy redirects, whitepaper. No /privacy page.
  • Footer SiteFooter.tsx + src/data/copy.ts: product / social / docs / disclaimer / law-enforcement. No Privacy link. No Terms link yet (#34 optional Terms href is separate and must stay named Terms).
  • CANONICAL_PRODUCT_URLS in src/content/invariants.ts is Bridge + DEX only.
  • AppProviders mounts wagmi (injected + optional walletConnect when VITE_WALLETCONNECT_PROJECT_ID is set, reconnectOnMount). No Connect UI. Transitive @reown/appkit / @coinbase/wallet-sdk in the lockfile; Coinbase connector not registered.
  • index.html loads Google Fonts on every paint.

DEX (code/cl8y-dex-terraclassic) — sibling PR

  • Footer: LegalFooterNotice.tsx (Security / Incidents / LP howto / Report) + cl8yProductLinks.ts (Homepage / Bridge). Product-link allowlist is only cl8y.com and bridge.cl8y.com — a Privacy href to terms.cl8y.com must not be forced through isAllowedCl8yProductHref.
  • Indexer Postgres stores chain-derived wallet addresses and trade aggregates (swap_events.sender/receiver, traders.address, LP provider, limit owner/maker). Public trader APIs: /api/v1/traders/{addr} and related. No IP / UA / cookie columns. Peer IP is an in-memory rate-limit key (PeerIpKeyExtractor; XFF not trusted). TraceLayer on HTTP.
  • WalletConnect via @walletconnect/legacy-client + cosmes; production CSP allowlists https://pulse.walletconnect.org. No Coinbase connector. Returning WC users auto-reconnect from cl8y_wallet_connection (useWallet.ts).
  • TermsGate: ConnectedTermsGate / legalClickwrap.ts, property dex.cl8y.com. Risk ack is localStorage cl8y-dex-risk-ack, not this notice.

Bridge (code/cl8y-bridge-monorepo) — sibling PR

  • Footer in packages/frontend/src/components/Layout.tsx: version / SHA / theme only. No legal links.
  • wagmi createConfig at module scope: walletConnect({ projectId, showQrModal: true }) when VITE_WC_PROJECT_ID is set; coinbaseWallet() always with no preference.telemetry: false. Reown AppKit is transitive only. Coinbase CCA / WC heartbeat are the idle third parties #165 gates.
  • TermsGate: BridgeTermsGate, property pin bridge.cl8y.com.
  • Operator/canceler process logs can include on-chain recipient / operator addresses. Peer IP is for Governor, not a PII table.

Agent-box connectors (first-party automated agents)

Allowlisted community / support messages can be turned into public engineering issues by first-party Cursor CLI agent boxes. Those boxes may invoke third-party model APIs and tool connectors (issue tracker; optional mail/social plugins when enabled).

Public outputs are anonymized issue text. The Privacy Notice must disclose this category (message content after redaction, public issue bodies, connector telemetry the vendors already document). It must not publish operational inventory (hosts, VMs, tokens, datastores, queue identifiers). Private-plane implementation stays in the private agent-control repo; do not copy runbooks into the notice.


Why this is needed

  1. T&Cs are not a Privacy Notice. Version 1.5 is a participant agreement hashed into signatures. Mixing privacy copy into that file forces a version bump and mass re-sign, and still would not be findable as /privacy.
  2. Processing already happens. TermsGate stores wallet/Telegram identities. The terms bot stores chat/user ids. The indexer publishes trader addresses. WC / Reown / Coinbase SDKs (and Google Fonts / Telegram JS) talk to third parties on page load or connect. HTTP tracers exist. Agent boxes file public issues from community messages. Visitors have no first-party document that says so.
  3. Guessed /privacy URLs currently impersonate product UI (#12). A real document at a stable HTTPS URL is the only honest redirect target; until then #12 correctly 404s.
  4. Consent work (#165) and product events (#1202) have nothing to link. A banner or event pipeline that invents policy prose is worse than silence. One canonical URL.
  5. Four footers, one constant. Splitting “legal file” vs “marketing link” vs “DEX link” vs “bridge link” produces drift (one host links T&Cs, another 404s, portal /privacy still renders sign-home).

This is legal document + static/portal serving + footer constants. No wallet crypto, no indexer schema change, no T&Cs oracle change, no CMP vendor.


Constraints and guardrails

  1. Do not edit TERMS_AND_CONDITIONS.txt or bump Version: 1.5. Privacy has its own label (e.g. Privacy-Notice-Version: 1.0) and its own hash if you publish one. Never bind Privacy SHA into build_acceptance_message.
  2. Do not call POST /update_terms for this document. scripts/publish-terms.sh stays terms-only.
  3. Canonical URL is compile-time https://terms.cl8y.com/privacy. URL + hardcoded origin and path. Never from query, hash, Referer, window.location, or env VITE_* redirect. Reject javascript:, data:, //, http, userinfo, ports.
  4. Worktree isolation. Notice + portal/API in this repo. Footer PRs in code/CL8Y-web, code/cl8y-dex-terraclassic, code/cl8y-bridge-monorepo. Do not patch siblings from a Legal checkout.
  5. Do not implement #12, #34, or #165 here. After merge, comment one line on #12 that the canonical URL exists so 301 is allowed. Do not close those issues from this ticket.
  6. Portal /privacy is not renderHome. Sign routes unchanged. Home still requires ?property= for T&Cs.
  7. Render as text (or safe static HTML). No innerHTML of API/user content. No dangerouslySetInnerHTML. Portal already uses text nodes for T&Cs (signShell.ts).
  8. Accurate inventory, no ops leak. Disclose categories, field names that already exist in public schemas, third-party SDK names, and “public chain data” vs “off-chain identity”. Do not name private hosts, VMs, orchestrators, disk snapshots, tokens, or internal databases.
  9. Anonymize people in the notice and in this issue’s follow-up comments. No chat logs, emails, or intake captures.
  10. Human-required legal copy. Engineering must not merge invented GDPR/CCPA conclusions, retention days, or “we do not collect X” claims that contradict the inventory above. Counsel reviews before production publish.
  11. Third parties named from code, not marketing guesses. WalletConnect / Reown Cloud (VITE_WC_PROJECT_ID / VITE_WALLETCONNECT_PROJECT_ID), Coinbase Wallet SDK / CCA (bridge coinbaseWallet()), Telegram telegram.org script + Login Widget / WebApp, Google Fonts on marketing, indexer public trader APIs, Legal API signature rows, agent-box model/tool vendors in generic terms.
  12. No CMP npm dependency and no OneTrust/Cookiebot. #165 owns the banner. This issue’s UI is a readable page + footer <a>.
  13. DEX product-link allowlist stays product-only. Privacy is a legal footer link (LegalFooterNotice or equivalent), not a third CL8Y_PRODUCT_LINKS entry unless you also extend ALLOWED_HOSTS and keep product vs legal distinct in tests.
  14. Do not add Privacy hash to clickwrap SDK message builders. Do not require Privacy acceptance to use the dapps (T&Cs / risk-ack / storage consent remain separate gates).
  15. Do not publish wallet addresses, Telegram ids, or signatures into the notice examples. Use placeholders (terra1…, 0x…, numeric user_id).
  16. Secrets. No ADMIN_TOKEN, WC project ids, or bot tokens in the notice, tests, or screenshots.

Relevant files

Path Why
PRIVACY_NOTICE.txt (new, repo root) Canonical source, sibling of TERMS_AND_CONDITIONS.txt
TERMS_AND_CONDITIONS.txt Do not modify
web/src/main.ts Add /privacy route; stop default-to-home for that path
web/src/pages/privacy.ts (new) Render notice as text nodes; footer/home chrome with Privacy + Terms distinction
web/src/pages/home.ts Add Privacy footer link on T&Cs home
web/src/signShell.ts Shared chrome: Privacy link on sign pages
web/index.html Title/description must not claim /privacy is the T&Cs portal
api/src/routes/terms.rs Do not overload; optional sibling api/src/routes/privacy.rs read-only content
api/src/message.rs Must remain T&Cs-only binding lines
api/src/routes/signatures.rs Inventory source for the notice; no schema change required
api/migrations/001_initial.sql, 002_bot_state.sql Inventory source
packages/cl8y-clickwrap/src/react/TermsGate.tsx Documented poll fields
README.md Document /privacy vs terms oracle
skills/ (new privacy-notice) Agent skill: never merge privacy into T&Cs; canonical URL constant
web/e2e/ Playwright, 5 workers, /privacy vs / vs /sign/*

Sibling code/CL8Y-web

Path Why
src/content/invariants.ts PRIVACY_NOTICE_URL = "https://terms.cl8y.com/privacy"
src/content/invariants.test.ts Exact HTTPS, no @ / redirectors / query
src/components/chrome/SiteFooter.tsx + src/data/copy.ts Label Privacy
src/components/ui/ExternalLink.tsx Existing helper
e2e/*.spec.ts Footer href; workers already 5

Sibling code/cl8y-dex-terraclassic

Path Why
frontend-dapp/src/components/legal/LegalFooterNotice.tsx Add Privacy next to Security / Incidents
frontend-dapp/src/components/legal/legalCopy.ts Constant href
frontend-dapp/src/utils/cl8yProductLinks.ts Do not overload product allowlist
docs/frontend.md Footer invariant
frontend Playwright / RTL data-testid for the Privacy link

Sibling code/cl8y-bridge-monorepo

Path Why
packages/frontend/src/components/Layout.tsx Footer Privacy link
packages/frontend/src/utils/clickwrap.ts Do not reuse property pin; Privacy is not clickwrap
docs/FRONTEND_BRIDGE_INVARIANTS.md New footer invariant
packages/frontend/e2e/ Footer href; 5 workers

1. Source document (this repo)

Add PRIVACY_NOTICE.txt with a header Privacy-Notice-Version: and Effective date:. Counsel-edited sections must cover, as separate headings:

  1. Who the notice is for and which first-party hosts it covers (cl8y.com, dex.cl8y.com, bridge.cl8y.com, terms.cl8y.com / api.terms.cl8y.com).
  2. Relationship to T&Cs 1.5 — this notice is not the Terms; accepting T&Cs is not a substitute for reading this notice; a T&Cs bump does not silently rewrite this notice.
  3. TermsGate / Legal API fields — status poll (property, network, account); wallet submit fields; Telegram widget / init_data verify-then-drop; what is stored in signatures.
  4. Telegram bot data — @cl8ytermsbot compliance tables (chat_id, user_id); reminders / kicks as processing purpose; no claim that names/photos are stored if they are not.
  5. Indexer — public chain-derived addresses and trade aggregates; public trader APIs; IPs not persisted as rows; optional future product events (#1202) described as “only if enabled and disclosed,” not shipped by this ticket.
  6. WalletConnect / Reown — project id, relay / pulse / verify / explorer; which hosts init WC at connect vs at module load (describe current code; do not promise #165 has shipped).
  7. Coinbase Wallet SDK — bridge registers coinbaseWallet(); marketing/DEX do not; CCA / telemetry as current-code risk until #165.
  8. Server logs — HTTP traces; IP for rate limits (Legal XFF policy; DEX/bridge peer IP); operator logs may contain on-chain addresses.
  9. Agent-box connectors — first-party automated agents + third-party model/tool connectors processing allowlisted community/support inputs into public anonymized issues; categories only; no inventory.
  10. Cookies / localStorage — first-party keys that exist (cl8y-theme, DEX risk-ack, bridge history, wagmi/WC session keys) as a factual list; point to #165 for refuse/accept. Do not invent a cookie policy page in this ticket (/cookies remains #12).
  11. What we do not sell / how to contact — use existing public contact contact@ceramicliberty.com already on the marketing footer. No new inbox product.
  12. Changes — how a Privacy version bump is published without a T&Cs re-sign.

Keep the body plain text. If HTML is served, generate from this file in the portal (text nodes / textContent), never innerHTML.

  • case "/privacy": in web/src/main.ts → dedicated renderer.
  • Optional GET /api/v1/privacy/latest/content returning text/plain from the file (or a dedicated table not terms_versions). If you skip the API, serving the static portal page is enough for AC.
  • Cache headers may be public. No auth.
  • Add a small footer on portal home + sign chrome: Privacy → /privacy; Terms remains the T&Cs flow. Do not label T&Cs as Privacy.
export const PRIVACY_NOTICE_URL = "https://terms.cl8y.com/privacy" as const;
  • Marketing: ExternalLink in SiteFooter, label Privacy, next to (not instead of) #34’s optional Terms href.
  • DEX: extra · link in LegalFooterNotice, data-testid="privacy-notice-link".
  • Bridge: footer row next to version/theme, same test id pattern.
  • All: rel="noopener noreferrer", https: only.

4. Coordination (not this implementer)

  • One-line technical comment on CL8Y-web#12 after the URL is live: +1 canonical notice https://terms.cl8y.com/privacy — 301 now allowed.
  • Do not close #12, #34, #165, or #1202.

Acceptance criteria

  • AC1. PRIVACY_NOTICE.txt exists at repo root with its own version header. TERMS_AND_CONDITIONS.txt still Version: 1.5 and byte-unchanged in the Privacy PR (or only an unrelated already-landed diff — this ticket must not edit it).
  • AC2. GET https://terms.cl8y.com/privacy (prod or preview with the same path) returns the notice as a document, not the T&Cs home / sign chooser. HTTP 200. Title/OG do not claim it is T&Cs.
  • AC3. Portal /, /sign/evm, /sign/terra-classic, /sign/solana, /sign/telegram still behave as today (property required on home; sign shells unchanged except a Privacy footer link).
  • AC4. Acceptance messages / SDK / api/src/message.rs still bind T&Cs Content-SHA256 only. No Privacy hash in signed messages. Existing signature rows remain valid.
  • AC5. Notice headings cover all nine processing categories in Recommended direction §1 items 3–9 (TermsGate, Telegram bot, indexer, WC/Reown, Coinbase SDK, server logs, agent-box connectors) plus the T&Cs-separation heading.
  • AC6. Marketing footer contains a Privacy link whose href is exactly https://terms.cl8y.com/privacy. It is not named Terms, Cookies, or Opt-out.
  • AC7. DEX footer contains the same exact href (legal footer, not product-link allowlist).
  • AC8. Bridge footer contains the same exact href.
  • AC9. Legal portal chrome on /privacy and sign/home includes the same path (absolute https://terms.cl8y.com/privacy or same-origin /privacy that resolves there).
  • AC10. No ADMIN_TOKEN, WC project id, bot token, or wallet/Telegram production identifiers in the notice, tests, or fixtures.
  • AC11. Counsel (human) recorded as having approved the copy before production publish. Engineering does not self-merge legal wording.
  • AC12. #12 routing work is not re-implemented here; siblings do not add a second invented /privacy HTML page on marketing/DEX/bridge that duplicates the notice body (link out, do not fork copy).

Test plan (functional paths)

# Path Expect
T1 GET /privacy on Legal portal (Playwright, 5 workers) Notice visible; not T&Cs chooser; no ?property= required
T2 GET / without property Unchanged home copy; Privacy link present
T3 GET /sign/evm?property=dex.cl8y.com Sign shell still loads terms T&Cs body; Privacy link in chrome; consent checkbox still T&Cs
T4 GET /sign/telegram, /sign/solana, /sign/terra-classic Same chrome Privacy link; sign logic unchanged
T5 Optional GET /api/v1/privacy/latest/content text/plain (or 404 if API skipped — then T1 is the only serve path); not a terms_versions row
T6 GET /api/v1/terms/latest?property=dex.cl8y.com Unchanged shape; content_sha256 still T&Cs
T7 Wallet/Telegram message unit tests (api + clickwrap) Byte-identical to pre-change fixtures
T8 CL8Y-web yarn test + yarn test:e2e --workers=5 Footer Privacy href constant; homepage CTAs unchanged
T9 DEX RTL / Playwright privacy-notice-link href exact; product links still only homepage/bridge; Swap / unaffected
T10 Bridge e2e --workers=5 Footer href exact; clickwrap specs still green
T11 gitleaks / secret scan No tokens in PRIVACY_NOTICE.txt
T12 Compare TERMS_AND_CONDITIONS.txt in the Legal PR diff Empty / no hunks
T13 Marketing /privacy Not required to serve the notice body (that is #12). If the sibling PR is only a footer link, /privacy on cl8y.com may still 404 per #12 until 301 is enabled

Legal portal Playwright: existing web/e2e plus new privacy spec, 5 workers.


Test plan (attack, hack, and abuse)

# Vector Expect
A1 Fold privacy copy into T&Cs 1.5 to “save a file” Forbidden; oracle would re-sign everyone; tests T12 fail
A2 Bind Privacy SHA into build_acceptance_message Forbidden; existing signatures would look stale or break verify
A3 POST /update_terms publishes the privacy file Forbidden; wrong oracle
A4 /privacy?redirect=https://phish.example / javascript: / //evil Ignored; page does not navigate off the constant
A5 Open redirect Location from query on footer click Impossible; compile-time href
A6 XSS via notice HTML (<script>, markdown HTML) Text nodes / escaped; no innerHTML
A7 Portal default route still renders T&Cs home at /privacy Fail AC2
A8 Label footer link “Terms & Privacy” pointing at T&Cs Forbidden; separate labels and URLs
A9 Duplicate full notice body into CL8Y-web / DEX / bridge Forbidden; forks drift; link only
A10 Claim “we never use WalletConnect / Coinbase / Telegram JS / IPs” Fail AC5 if code still does; notice must match inventory
A11 Publish WC project ids, ADMIN_TOKEN, bot tokens, or live Telegram user ids Fail AC10 / gitleaks
A12 Publish operational inventory (hosts, VMs, orchestrators, snapshots, internal DB files, queue ids) Forbidden
A13 Clickwrap signed_latest used as Privacy consent Forbidden; separate documents and gates
A14 Marketing mounts TermsGate “because we added Privacy” Forbidden (#34 exception)
A15 CORS * so anyone can POST a fake privacy document Privacy serve is GET/static; no admin write without existing admin auth story; do not add a new unauthenticated write
A16 Replace #12 404 with SPA fallback that paints the homepage at /privacy on marketing Forbidden; siblings link out only
A17 Phishing clone: footer href from window.location.hostname + "/privacy" Forbidden; pin terms.cl8y.com
A18 Agent-box section names private repo internals or copies chat logs Forbidden; categories only
A19 Indexer described as “anonymous” while /traders/{addr} is public Notice must say trader routes are public chain data
A20 Iframe the notice on a third-party host to clickjack accept No accept control on /privacy. Portal/marketing frame-ancestors / clickjacking headers stay on their own issues

Verification criteria

  1. Document: PRIVACY_NOTICE.txt in this repo; counsel approval recorded on the PR (not in a public paste of email).
  2. Portal: Playwright T1–T4 green (web e2e, 5 workers). /privacy is the notice; sign routes still T&Cs.
  3. Oracle untouched: TERMS_AND_CONDITIONS.txt diff empty; message unit tests green; a previously valid T&Cs signature still verifies.
  4. Footers: live or preview href="https://terms.cl8y.com/privacy" on all four hosts (portal may use same-origin /privacy that is that document).
  5. Grep: no Privacy SHA in api/src/message.rs / clickwrap message.ts. No TermsGate added to CL8Y-web.
  6. Siblings: CL8Y-web yarn test && yarn typecheck && yarn test:e2e --workers=5; DEX frontend tests for the legal footer; bridge e2e clickwrap + footer.
  7. Coordination: one-line +1 on CL8Y-web#12 with the canonical URL after prod/preview serve works. #12, #34, #165 remain open until their own AC lands.

Out of scope

  • SPA reserved-path 404/301 tables (CL8Y-web#12, DEX nginx, bridge path="*").
  • Storage-consent banner, lazy WC/Coinbase init, CCA/pulse gating (bridge#165).
  • TermsGate coverage / CORS / bridge.cl8y.com property ops (#34, bridge#134).
  • DEX product-event pipeline (#1202) and indexer redaction tickets (#1205, #1206).
  • Rewriting T&Cs 1.5, message format, or content_sha256 oracle (#6).
  • Cookie policy page, opt-out form, CMP vendor, Google Fonts removal (fonts are inventory + #165/#12, not this body).
  • Voting host footer (vote.cl8y.com) unless a follow-up issue adds that row.
  • Implementing or reviewing agent-box internals, intake, or deploy.
  • Choosing models, SKUs, images, or hosts.

References

  • T&Cs source: TERMS_AND_CONDITIONS.txt (Version: 1.5)
  • TermsGate SDK: packages/cl8y-clickwrap/src/react/TermsGate.tsx
  • Signature API: api/src/routes/signatures.rs
  • Portal router: web/src/main.ts
  • SPA fallthrough: CL8Y-web#12
  • TermsGate matrix: #34
  • Storage consent: cl8y-bridge-monorepo#165
  • Product events: cl8y-dex-terraclassic#1202
  • Portal / API: https://terms.cl8y.com · https://api.terms.cl8y.com
## Summary Publish a **dedicated Privacy Notice** as a first-party HTTPS document, **separate from T&Cs Version 1.5**, then link it from the four product footers. T&Cs `Version: 1.5` (`TERMS_AND_CONDITIONS.txt`) has no privacy / cookies / data-processing section. TermsGate, `@cl8ytermsbot`, the DEX indexer, WalletConnect / Reown, Coinbase Wallet SDK, request logs, and first-party agent-box tool connectors already process data. None of that is disclosed in a document a visitor can open from the footer. This issue is the **canonical notice + `https://terms.cl8y.com/privacy` + footer links**. It does **not** absorb SPA legal-guess routing, storage-consent / idle SDK init, or TermsGate coverage parity. Those stay on the issues named below. Bundle (do not split): 1. **Author and version** a Privacy Notice in this repo, independent of the terms oracle / `content_sha256` re-sign path. 2. **Serve** it at compile-time `https://terms.cl8y.com/privacy` (portal route; optional read-only API content route that is **not** bound into acceptance messages). 3. **Footer links** labeled **Privacy** (not Terms) on `cl8y.com`, `dex.cl8y.com`, `bridge.cl8y.com`, and `terms.cl8y.com`, all pointing at that same HTTPS URL. **Decisions (do not re-litigate unless product changes them):** | Surface | Decision | |---------|----------| | T&Cs 1.5 | Unchanged. Do **not** fold privacy prose into `TERMS_AND_CONDITIONS.txt`. | | Canonical URL | `https://terms.cl8y.com/privacy` (exact origin + path, HTTPS, no query/hash). | | Marketing `cl8y.com` | Footer **Privacy** link only. Still **no** TermsGate ([#34](https://git.cl8y.com/code/cl8y-ecosystem-legal/issues/34)). | | DEX / Bridge | Footer **Privacy** link. Do not treat clickwrap or the DEX risk modal as this notice. | | Legal portal | `/privacy` must **not** fall through to T&Cs home (`renderHome`). | | Counsel | Legal copy is **human-required** before merge. Engineering supplies an accurate processing inventory; counsel owns wording, lawful-basis language, and retention periods. | Not a duplicate of: - [CL8Y-web#12](https://git.cl8y.com/code/CL8Y-web/issues/12) — reserved-path **routing** (`/privacy` must not paint Swap / Transfer / homepage). That issue says 404 until a canonical notice exists; authoring the notice is out of scope there. After this URL exists, #12 may 301 reserved paths **to this constant**. - [#34](https://git.cl8y.com/code/cl8y-ecosystem-legal/issues/34) — TermsGate coverage matrix. Out of scope there: “Authoring a Privacy Notice”. - [cl8y-bridge-monorepo#165](https://git.cl8y.com/code/cl8y-bridge-monorepo/issues/165) — storage consent that stops idle WalletConnect pulse and Coinbase CCA. That issue must **link** this notice when it exists; it must not author policy prose. - [cl8y-dex-terraclassic#1202](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/1202) — privacy-reviewed product events. Events must stay consistent with this notice; this issue does not ship the event pipeline. --- ## Current codebase ### Legal platform (`code/cl8y-ecosystem-legal`) — this worktree | Piece | Behavior today | |-------|----------------| | T&Cs | [`TERMS_AND_CONDITIONS.txt`](TERMS_AND_CONDITIONS.txt) `Version: 1.5`, effective 10 August 2026. Privacy appears only as incidental “privacy laws” language. Hash-aware oracle in `api/src/terms/sync.rs` binds `content_sha256` into wallet/Telegram acceptance messages (`api/src/message.rs`). | | Portal | `https://terms.cl8y.com`. [`web/src/main.ts`](web/src/main.ts) routes `/sign/evm|solana|terra-classic|telegram` and **defaults every other path to `renderHome`** (T&Cs acceptance). **No footer. No `/privacy`.** [`web/index.html`](web/index.html) loads `https://telegram.org/js/telegram-web-app.js` on **every** page, including ordinary browser visits. | | API | `https://api.terms.cl8y.com`. Terms routes: `GET /api/v1/terms/latest`, `/terms/latest/content`, `/terms/{version_label}`. Signature routes: `GET /signatures/status`, `POST /signatures/wallet`, `POST /signatures/telegram`. **No privacy document route.** | | TermsGate SDK | `packages/cl8y-clickwrap` — `TermsGate` polls `property` + `network` + `account`. Networks: `EVM` / `TerraClassic` / `Solana` / `Telegram`. | | WalletConnect / Reown | Legal-owned `VITE_WC_PROJECT_ID` (Reown Cloud) for `/sign/evm` and Galaxy Station WC v2 on `/sign/terra-classic` (`web/src/evm/walletConnect.ts`, `web/src/terra/walletConnect.ts`). No `@reown/*` direct import; no Coinbase Wallet SDK in this repo. | | Request logs | `tower_http` `TraceLayer` on the API. Client IP is used for **in-memory rate limits only** (`api/src/rate_limit.rs`: TCP peer by default; rightmost XFF hop only inside `TRUSTED_PROXY_CIDRS`). IPs are **not** columns on `signatures` / `terms_versions` / bot tables. | **TermsGate / signature fields actually persisted** (`api/migrations/001_initial.sql`, `WalletSubmitBody` / `TelegramSubmitBody` in `api/src/routes/signatures.rs`): - Wallet: `property`, `network`, `account_id`, `message`, `signature`, optional `pubkey`, `client_timestamp`, optional `version_label`; proof JSON `{ type, signature, pubkey }`. - Telegram submit (verify-then-drop): widget `id`, `first_name`, `last_name`, `username`, `photo_url`, `auth_date`, `hash`, or Mini App `init_data`. Stored: `account_id` = Telegram user id, `account_display` = `@username` if present, proof `{ type, auth_date }` or `{ type, hash, auth_date }`. Names/photos are used for hash verify, then dropped (not columns). - Canonical message binding lines: `Property`, `Network`, `Account`, `Content-SHA256`, `Accepted at (UTC)` — **T&Cs bytes only**. Do not add Privacy Notice hash to this message. **Telegram enforcement bot** (`bot/`, public README name `@cl8ytermsbot`): - `bot_chat_state`: `chat_id`, `last_known_version`, `last_reminder_at` - `bot_member_compliance`: `chat_id`, `user_id`, `required_since` - Numeric ids only in those tables. Fail-closed on Legal API errors ([#5](https://git.cl8y.com/code/cl8y-ecosystem-legal/issues/5)). There is **no** `PRIVACY_NOTICE*` file and **no** privacy version table. ### Marketing (`code/CL8Y-web`) — sibling PR - Routes in `src/app/index.tsx`: `/`, blog, legacy redirects, whitepaper. **No** `/privacy` page. - Footer [`SiteFooter.tsx`](https://git.cl8y.com/code/CL8Y-web/src/branch/main/src/components/chrome/SiteFooter.tsx) + `src/data/copy.ts`: product / social / docs / disclaimer / law-enforcement. **No Privacy link. No Terms link yet** (#34 optional Terms href is separate and must stay named Terms). - `CANONICAL_PRODUCT_URLS` in `src/content/invariants.ts` is Bridge + DEX only. - `AppProviders` mounts wagmi (`injected` + optional `walletConnect` when `VITE_WALLETCONNECT_PROJECT_ID` is set, `reconnectOnMount`). No Connect UI. Transitive `@reown/appkit` / `@coinbase/wallet-sdk` in the lockfile; Coinbase connector **not** registered. - `index.html` loads Google Fonts on every paint. ### DEX (`code/cl8y-dex-terraclassic`) — sibling PR - Footer: [`LegalFooterNotice.tsx`](https://git.cl8y.com/code/cl8y-dex-terraclassic/src/branch/main/frontend-dapp/src/components/legal/LegalFooterNotice.tsx) (Security / Incidents / LP howto / Report) + [`cl8yProductLinks.ts`](https://git.cl8y.com/code/cl8y-dex-terraclassic/src/branch/main/frontend-dapp/src/utils/cl8yProductLinks.ts) (Homepage / Bridge). Product-link allowlist is **only** `cl8y.com` and `bridge.cl8y.com` — a Privacy href to `terms.cl8y.com` must **not** be forced through `isAllowedCl8yProductHref`. - Indexer Postgres stores **chain-derived** wallet addresses and trade aggregates (`swap_events.sender/receiver`, `traders.address`, LP `provider`, limit `owner`/`maker`). Public trader APIs: `/api/v1/traders/{addr}` and related. **No** IP / UA / cookie columns. Peer IP is an in-memory rate-limit key (`PeerIpKeyExtractor`; XFF not trusted). `TraceLayer` on HTTP. - WalletConnect via `@walletconnect/legacy-client` + cosmes; production CSP allowlists `https://pulse.walletconnect.org`. No Coinbase connector. Returning WC users auto-reconnect from `cl8y_wallet_connection` (`useWallet.ts`). - TermsGate: `ConnectedTermsGate` / `legalClickwrap.ts`, property `dex.cl8y.com`. Risk ack is localStorage `cl8y-dex-risk-ack`, not this notice. ### Bridge (`code/cl8y-bridge-monorepo`) — sibling PR - Footer in `packages/frontend/src/components/Layout.tsx`: version / SHA / theme only. **No legal links.** - wagmi `createConfig` at module scope: `walletConnect({ projectId, showQrModal: true })` when `VITE_WC_PROJECT_ID` is set; **`coinbaseWallet()` always** with no `preference.telemetry: false`. Reown AppKit is transitive only. Coinbase CCA / WC heartbeat are the idle third parties #165 gates. - TermsGate: `BridgeTermsGate`, property pin `bridge.cl8y.com`. - Operator/canceler process logs can include **on-chain recipient / operator addresses**. Peer IP is for Governor, not a PII table. ### Agent-box connectors (first-party automated agents) Allowlisted community / support messages can be turned into **public** engineering issues by first-party Cursor CLI agent boxes. Those boxes may invoke third-party model APIs and tool connectors (issue tracker; optional mail/social plugins when enabled). Public outputs are anonymized issue text. The Privacy Notice must disclose this **category** (message content after redaction, public issue bodies, connector telemetry the vendors already document). It must **not** publish operational inventory (hosts, VMs, tokens, datastores, queue identifiers). Private-plane implementation stays in the private agent-control repo; do not copy runbooks into the notice. --- ## Why this is needed 1. **T&Cs are not a Privacy Notice.** Version 1.5 is a participant agreement hashed into signatures. Mixing privacy copy into that file forces a version bump and mass re-sign, and still would not be findable as `/privacy`. 2. **Processing already happens.** TermsGate stores wallet/Telegram identities. The terms bot stores chat/user ids. The indexer publishes trader addresses. WC / Reown / Coinbase SDKs (and Google Fonts / Telegram JS) talk to third parties on page load or connect. HTTP tracers exist. Agent boxes file public issues from community messages. Visitors have no first-party document that says so. 3. **Guessed `/privacy` URLs currently impersonate product UI** (#12). A real document at a stable HTTPS URL is the only honest redirect target; until then #12 correctly 404s. 4. **Consent work (#165) and product events (#1202) have nothing to link.** A banner or event pipeline that invents policy prose is worse than silence. One canonical URL. 5. **Four footers, one constant.** Splitting “legal file” vs “marketing link” vs “DEX link” vs “bridge link” produces drift (one host links T&Cs, another 404s, portal `/privacy` still renders sign-home). This is **legal document + static/portal serving + footer constants**. No wallet crypto, no indexer schema change, no T&Cs oracle change, no CMP vendor. --- ## Constraints and guardrails 1. **Do not edit `TERMS_AND_CONDITIONS.txt` or bump `Version: 1.5`.** Privacy has its own label (e.g. `Privacy-Notice-Version: 1.0`) and its own hash if you publish one. Never bind Privacy SHA into `build_acceptance_message`. 2. **Do not call `POST /update_terms` for this document.** `scripts/publish-terms.sh` stays terms-only. 3. **Canonical URL is compile-time `https://terms.cl8y.com/privacy`.** `URL` + hardcoded origin and path. Never from query, hash, `Referer`, `window.location`, or env `VITE_*` redirect. Reject `javascript:`, `data:`, `//`, http, userinfo, ports. 4. **Worktree isolation.** Notice + portal/API in **this** repo. Footer PRs in `code/CL8Y-web`, `code/cl8y-dex-terraclassic`, `code/cl8y-bridge-monorepo`. Do not patch siblings from a Legal checkout. 5. **Do not implement #12, #34, or #165 here.** After merge, comment one line on #12 that the canonical URL exists so 301 is allowed. Do not close those issues from this ticket. 6. **Portal `/privacy` is not `renderHome`.** Sign routes unchanged. Home still requires `?property=` for T&Cs. 7. **Render as text (or safe static HTML).** No `innerHTML` of API/user content. No `dangerouslySetInnerHTML`. Portal already uses text nodes for T&Cs (`signShell.ts`). 8. **Accurate inventory, no ops leak.** Disclose categories, field names that already exist in public schemas, third-party SDK names, and “public chain data” vs “off-chain identity”. Do **not** name private hosts, VMs, orchestrators, disk snapshots, tokens, or internal databases. 9. **Anonymize people** in the notice and in this issue’s follow-up comments. No chat logs, emails, or intake captures. 10. **Human-required legal copy.** Engineering must not merge invented GDPR/CCPA conclusions, retention days, or “we do not collect X” claims that contradict the inventory above. Counsel reviews before production publish. 11. **Third parties named from code, not marketing guesses.** WalletConnect / Reown Cloud (`VITE_WC_PROJECT_ID` / `VITE_WALLETCONNECT_PROJECT_ID`), Coinbase Wallet SDK / CCA (bridge `coinbaseWallet()`), Telegram `telegram.org` script + Login Widget / WebApp, Google Fonts on marketing, indexer public trader APIs, Legal API signature rows, agent-box model/tool vendors in generic terms. 12. **No CMP npm dependency** and no OneTrust/Cookiebot. #165 owns the banner. This issue’s UI is a readable page + footer `<a>`. 13. **DEX product-link allowlist stays product-only.** Privacy is a **legal** footer link (`LegalFooterNotice` or equivalent), not a third `CL8Y_PRODUCT_LINKS` entry unless you also extend `ALLOWED_HOSTS` **and** keep product vs legal distinct in tests. 14. **Do not** add Privacy hash to clickwrap SDK message builders. Do not require Privacy acceptance to use the dapps (T&Cs / risk-ack / storage consent remain separate gates). 15. **Do not** publish wallet addresses, Telegram ids, or signatures into the notice examples. Use placeholders (`terra1…`, `0x…`, numeric `user_id`). 16. **Secrets.** No `ADMIN_TOKEN`, WC project ids, or bot tokens in the notice, tests, or screenshots. --- ## Relevant files ### This repo (`code/cl8y-ecosystem-legal`) | Path | Why | |------|-----| | `PRIVACY_NOTICE.txt` (new, repo root) | Canonical source, sibling of `TERMS_AND_CONDITIONS.txt` | | `TERMS_AND_CONDITIONS.txt` | **Do not modify** | | `web/src/main.ts` | Add `/privacy` route; stop default-to-home for that path | | `web/src/pages/privacy.ts` (new) | Render notice as text nodes; footer/home chrome with Privacy + Terms distinction | | `web/src/pages/home.ts` | Add Privacy footer link on T&Cs home | | `web/src/signShell.ts` | Shared chrome: Privacy link on sign pages | | `web/index.html` | Title/description must not claim `/privacy` is the T&Cs portal | | `api/src/routes/terms.rs` | Do not overload; optional sibling `api/src/routes/privacy.rs` read-only content | | `api/src/message.rs` | Must remain T&Cs-only binding lines | | `api/src/routes/signatures.rs` | Inventory source for the notice; no schema change required | | `api/migrations/001_initial.sql`, `002_bot_state.sql` | Inventory source | | `packages/cl8y-clickwrap/src/react/TermsGate.tsx` | Documented poll fields | | `README.md` | Document `/privacy` vs terms oracle | | `skills/` (new `privacy-notice`) | Agent skill: never merge privacy into T&Cs; canonical URL constant | | `web/e2e/` | Playwright, **5 workers**, `/privacy` vs `/` vs `/sign/*` | ### Sibling `code/CL8Y-web` | Path | Why | |------|-----| | `src/content/invariants.ts` | `PRIVACY_NOTICE_URL = "https://terms.cl8y.com/privacy"` | | `src/content/invariants.test.ts` | Exact HTTPS, no `@` / redirectors / query | | `src/components/chrome/SiteFooter.tsx` + `src/data/copy.ts` | Label **Privacy** | | `src/components/ui/ExternalLink.tsx` | Existing helper | | `e2e/*.spec.ts` | Footer href; workers already 5 | ### Sibling `code/cl8y-dex-terraclassic` | Path | Why | |------|-----| | `frontend-dapp/src/components/legal/LegalFooterNotice.tsx` | Add Privacy next to Security / Incidents | | `frontend-dapp/src/components/legal/legalCopy.ts` | Constant href | | `frontend-dapp/src/utils/cl8yProductLinks.ts` | Do **not** overload product allowlist | | `docs/frontend.md` | Footer invariant | | frontend Playwright / RTL | `data-testid` for the Privacy link | ### Sibling `code/cl8y-bridge-monorepo` | Path | Why | |------|-----| | `packages/frontend/src/components/Layout.tsx` | Footer Privacy link | | `packages/frontend/src/utils/clickwrap.ts` | Do not reuse property pin; Privacy is not clickwrap | | `docs/FRONTEND_BRIDGE_INVARIANTS.md` | New footer invariant | | `packages/frontend/e2e/` | Footer href; 5 workers | --- ## Recommended direction ### 1. Source document (this repo) Add `PRIVACY_NOTICE.txt` with a header `Privacy-Notice-Version:` and `Effective date:`. Counsel-edited sections **must** cover, as separate headings: 1. **Who the notice is for** and which first-party hosts it covers (`cl8y.com`, `dex.cl8y.com`, `bridge.cl8y.com`, `terms.cl8y.com` / `api.terms.cl8y.com`). 2. **Relationship to T&Cs 1.5** — this notice is not the Terms; accepting T&Cs is not a substitute for reading this notice; a T&Cs bump does not silently rewrite this notice. 3. **TermsGate / Legal API fields** — status poll (`property`, `network`, `account`); wallet submit fields; Telegram widget / `init_data` verify-then-drop; what is stored in `signatures`. 4. **Telegram bot data** — `@cl8ytermsbot` compliance tables (`chat_id`, `user_id`); reminders / kicks as processing purpose; no claim that names/photos are stored if they are not. 5. **Indexer** — public chain-derived addresses and trade aggregates; public trader APIs; IPs not persisted as rows; optional future product events (#1202) described as “only if enabled and disclosed,” not shipped by this ticket. 6. **WalletConnect / Reown** — project id, relay / pulse / verify / explorer; which hosts init WC at connect vs at module load (describe current code; do not promise #165 has shipped). 7. **Coinbase Wallet SDK** — bridge registers `coinbaseWallet()`; marketing/DEX do not; CCA / telemetry as current-code risk until #165. 8. **Server logs** — HTTP traces; IP for rate limits (Legal XFF policy; DEX/bridge peer IP); operator logs may contain on-chain addresses. 9. **Agent-box connectors** — first-party automated agents + third-party model/tool connectors processing allowlisted community/support inputs into public anonymized issues; categories only; no inventory. 10. **Cookies / localStorage** — first-party keys that exist (`cl8y-theme`, DEX risk-ack, bridge history, wagmi/WC session keys) as a factual list; point to #165 for refuse/accept. Do not invent a cookie policy page in this ticket (`/cookies` remains #12). 11. **What we do not sell / how to contact** — use existing public contact `contact@ceramicliberty.com` already on the marketing footer. No new inbox product. 12. **Changes** — how a Privacy version bump is published **without** a T&Cs re-sign. Keep the body plain text. If HTML is served, generate from this file in the portal (text nodes / `textContent`), never `innerHTML`. ### 2. Serve on the Legal portal - `case "/privacy":` in `web/src/main.ts` → dedicated renderer. - Optional `GET /api/v1/privacy/latest/content` returning `text/plain` from the file (or a dedicated table **not** `terms_versions`). If you skip the API, serving the static portal page is enough for AC. - Cache headers may be public. No auth. - Add a small footer on portal home + sign chrome: **Privacy** → `/privacy`; **Terms** remains the T&Cs flow. Do not label T&Cs as Privacy. ### 3. Footer siblings (separate PRs, same constant) ```ts export const PRIVACY_NOTICE_URL = "https://terms.cl8y.com/privacy" as const; ``` - Marketing: `ExternalLink` in `SiteFooter`, label **Privacy**, next to (not instead of) #34’s optional **Terms** href. - DEX: extra ` · ` link in `LegalFooterNotice`, `data-testid="privacy-notice-link"`. - Bridge: footer row next to version/theme, same test id pattern. - All: `rel="noopener noreferrer"`, `https:` only. ### 4. Coordination (not this implementer) - One-line technical comment on CL8Y-web#12 after the URL is live: `+1 canonical notice https://terms.cl8y.com/privacy — 301 now allowed`. - Do not close #12, #34, #165, or #1202. --- ## Acceptance criteria - [ ] **AC1.** `PRIVACY_NOTICE.txt` exists at repo root with its own version header. `TERMS_AND_CONDITIONS.txt` still `Version: 1.5` and byte-unchanged in the Privacy PR (or only an unrelated already-landed diff — this ticket must not edit it). - [ ] **AC2.** `GET https://terms.cl8y.com/privacy` (prod or preview with the same path) returns the notice as a document, **not** the T&Cs home / sign chooser. HTTP 200. Title/OG do not claim it is T&Cs. - [ ] **AC3.** Portal `/`, `/sign/evm`, `/sign/terra-classic`, `/sign/solana`, `/sign/telegram` still behave as today (property required on home; sign shells unchanged except a Privacy footer link). - [ ] **AC4.** Acceptance messages / SDK / `api/src/message.rs` still bind **T&Cs** `Content-SHA256` only. No Privacy hash in signed messages. Existing signature rows remain valid. - [ ] **AC5.** Notice headings cover all nine processing categories in Recommended direction §1 items 3–9 (TermsGate, Telegram bot, indexer, WC/Reown, Coinbase SDK, server logs, agent-box connectors) plus the T&Cs-separation heading. - [ ] **AC6.** Marketing footer contains a **Privacy** link whose `href` is exactly `https://terms.cl8y.com/privacy`. It is not named Terms, Cookies, or Opt-out. - [ ] **AC7.** DEX footer contains the same exact href (legal footer, not product-link allowlist). - [ ] **AC8.** Bridge footer contains the same exact href. - [ ] **AC9.** Legal portal chrome on `/privacy` and sign/home includes the same path (absolute `https://terms.cl8y.com/privacy` or same-origin `/privacy` that resolves there). - [ ] **AC10.** No `ADMIN_TOKEN`, WC project id, bot token, or wallet/Telegram production identifiers in the notice, tests, or fixtures. - [ ] **AC11.** Counsel (human) recorded as having approved the copy **before** production publish. Engineering does not self-merge legal wording. - [ ] **AC12.** `#12` routing work is not re-implemented here; siblings do not add a second invented `/privacy` HTML page on marketing/DEX/bridge that duplicates the notice body (link out, do not fork copy). --- ## Test plan (functional paths) | # | Path | Expect | |---|------|--------| | T1 | `GET /privacy` on Legal portal (Playwright, 5 workers) | Notice visible; not T&Cs chooser; no `?property=` required | | T2 | `GET /` without property | Unchanged home copy; Privacy link present | | T3 | `GET /sign/evm?property=dex.cl8y.com` | Sign shell still loads terms **T&Cs** body; Privacy link in chrome; consent checkbox still T&Cs | | T4 | `GET /sign/telegram`, `/sign/solana`, `/sign/terra-classic` | Same chrome Privacy link; sign logic unchanged | | T5 | Optional `GET /api/v1/privacy/latest/content` | `text/plain` (or 404 if API skipped — then T1 is the only serve path); **not** a `terms_versions` row | | T6 | `GET /api/v1/terms/latest?property=dex.cl8y.com` | Unchanged shape; `content_sha256` still T&Cs | | T7 | Wallet/Telegram message unit tests (`api` + clickwrap) | Byte-identical to pre-change fixtures | | T8 | CL8Y-web `yarn test` + `yarn test:e2e --workers=5` | Footer Privacy href constant; homepage CTAs unchanged | | T9 | DEX RTL / Playwright | `privacy-notice-link` href exact; product links still only homepage/bridge; Swap `/` unaffected | | T10 | Bridge e2e `--workers=5` | Footer href exact; clickwrap specs still green | | T11 | `gitleaks` / secret scan | No tokens in `PRIVACY_NOTICE.txt` | | T12 | Compare `TERMS_AND_CONDITIONS.txt` in the Legal PR diff | Empty / no hunks | | T13 | Marketing `/privacy` | **Not** required to serve the notice body (that is #12). If the sibling PR is only a footer link, `/privacy` on `cl8y.com` may still 404 per #12 until 301 is enabled | Legal portal Playwright: existing `web/e2e` plus new privacy spec, **5 workers**. --- ## Test plan (attack, hack, and abuse) | # | Vector | Expect | |---|--------|--------| | A1 | Fold privacy copy into T&Cs 1.5 to “save a file” | Forbidden; oracle would re-sign everyone; tests T12 fail | | A2 | Bind Privacy SHA into `build_acceptance_message` | Forbidden; existing signatures would look stale or break verify | | A3 | `POST /update_terms` publishes the privacy file | Forbidden; wrong oracle | | A4 | `/privacy?redirect=https://phish.example` / `javascript:` / `//evil` | Ignored; page does not navigate off the constant | | A5 | Open redirect `Location` from query on footer click | Impossible; compile-time href | | A6 | XSS via notice HTML (`<script>`, markdown HTML) | Text nodes / escaped; no `innerHTML` | | A7 | Portal default route still renders T&Cs home at `/privacy` | Fail AC2 | | A8 | Label footer link “Terms & Privacy” pointing at T&Cs | Forbidden; separate labels and URLs | | A9 | Duplicate full notice body into CL8Y-web / DEX / bridge | Forbidden; forks drift; link only | | A10 | Claim “we never use WalletConnect / Coinbase / Telegram JS / IPs” | Fail AC5 if code still does; notice must match inventory | | A11 | Publish WC project ids, `ADMIN_TOKEN`, bot tokens, or live Telegram user ids | Fail AC10 / gitleaks | | A12 | Publish operational inventory (hosts, VMs, orchestrators, snapshots, internal DB files, queue ids) | Forbidden | | A13 | Clickwrap `signed_latest` used as Privacy consent | Forbidden; separate documents and gates | | A14 | Marketing mounts TermsGate “because we added Privacy” | Forbidden (#34 exception) | | A15 | CORS `*` so anyone can POST a fake privacy document | Privacy serve is GET/static; no admin write without existing admin auth story; do not add a new unauthenticated write | | A16 | Replace #12 404 with SPA fallback that paints the homepage at `/privacy` on marketing | Forbidden; siblings link out only | | A17 | Phishing clone: footer href from `window.location.hostname + "/privacy"` | Forbidden; pin `terms.cl8y.com` | | A18 | Agent-box section names private repo internals or copies chat logs | Forbidden; categories only | | A19 | Indexer described as “anonymous” while `/traders/{addr}` is public | Notice must say trader routes are public chain data | | A20 | Iframe the notice on a third-party host to clickjack accept | No accept control on `/privacy`. Portal/marketing `frame-ancestors` / clickjacking headers stay on their own issues | --- ## Verification criteria 1. **Document:** `PRIVACY_NOTICE.txt` in this repo; counsel approval recorded on the PR (not in a public paste of email). 2. **Portal:** Playwright T1–T4 green (`web` e2e, 5 workers). `/privacy` is the notice; sign routes still T&Cs. 3. **Oracle untouched:** `TERMS_AND_CONDITIONS.txt` diff empty; message unit tests green; a previously valid T&Cs signature still verifies. 4. **Footers:** live or preview `href="https://terms.cl8y.com/privacy"` on all four hosts (portal may use same-origin `/privacy` that is that document). 5. **Grep:** no Privacy SHA in `api/src/message.rs` / clickwrap `message.ts`. No `TermsGate` added to CL8Y-web. 6. **Siblings:** CL8Y-web `yarn test && yarn typecheck && yarn test:e2e --workers=5`; DEX frontend tests for the legal footer; bridge e2e clickwrap + footer. 7. **Coordination:** one-line `+1` on CL8Y-web#12 with the canonical URL after prod/preview serve works. #12, #34, #165 remain open until their own AC lands. --- ## Out of scope - SPA reserved-path 404/301 tables (CL8Y-web#12, DEX nginx, bridge `path="*"`). - Storage-consent banner, lazy WC/Coinbase init, CCA/pulse gating (bridge#165). - TermsGate coverage / CORS / `bridge.cl8y.com` property ops (#34, bridge#134). - DEX product-event pipeline (#1202) and indexer redaction tickets (#1205, #1206). - Rewriting T&Cs 1.5, message format, or `content_sha256` oracle (#6). - Cookie policy page, opt-out form, CMP vendor, Google Fonts removal (fonts are inventory + #165/#12, not this body). - Voting host footer (`vote.cl8y.com`) unless a follow-up issue adds that row. - Implementing or reviewing agent-box internals, intake, or deploy. - Choosing models, SKUs, images, or hosts. ## References - T&Cs source: `TERMS_AND_CONDITIONS.txt` (`Version: 1.5`) - TermsGate SDK: `packages/cl8y-clickwrap/src/react/TermsGate.tsx` - Signature API: `api/src/routes/signatures.rs` - Portal router: `web/src/main.ts` - SPA fallthrough: [CL8Y-web#12](https://git.cl8y.com/code/CL8Y-web/issues/12) - TermsGate matrix: [#34](https://git.cl8y.com/code/cl8y-ecosystem-legal/issues/34) - Storage consent: [cl8y-bridge-monorepo#165](https://git.cl8y.com/code/cl8y-bridge-monorepo/issues/165) - Product events: [cl8y-dex-terraclassic#1202](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/1202) - Portal / API: `https://terms.cl8y.com` · `https://api.terms.cl8y.com`
Author
Owner

Confirmed the notice URL should be https://terms.cl8y.com/privacy (not the T&Cs home)

Confirmed the notice URL should be https://terms.cl8y.com/privacy (not the T&Cs home)
Author
Owner

cl8y-pm S1 opt-out card completed: required. Product events stay blocked until an opt-out exists. Privacy Notice unlock card was already done; this issue remains the notice + terms.cl8y.com/privacy work.

cl8y-pm S1 opt-out card completed: **required**. Product events stay blocked until an opt-out exists. Privacy Notice unlock card was already done; this issue remains the notice + terms.cl8y.com/privacy work.
Author
Owner

Live URL check from cl8y-pm ops 2026-09-09. No secret values.

Operator completed a phone card to confirm https://terms.cl8y.com/privacy and wrote approve, while noting the page still looks like the T&Cs acceptance portal (property query copy).

Independent GET of /privacy and / both 200 with title CL8Y Legal — Terms & Conditions. No Privacy Notice document at that path yet. /privacy still falls through to T&Cs home.

Unlock-to-publish is not clean until this route serves a dedicated notice.

Live URL check from cl8y-pm ops 2026-09-09. No secret values. Operator completed a phone card to confirm `https://terms.cl8y.com/privacy` and wrote approve, while noting the page still looks like the T&Cs acceptance portal (property query copy). Independent GET of `/privacy` and `/` both 200 with title `CL8Y Legal — Terms & Conditions`. No Privacy Notice document at that path yet. `/privacy` still falls through to T&Cs home. Unlock-to-publish is not clean until this route serves a dedicated notice.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-ecosystem-legal#35
No description provided.