Implements docs/read-markets-compliance-changes.md as flag-gated changes (no deletions) so paused features stay in the tree for future re-enable. All four flags default False so a fresh deploy is compliance-safe. - New env flags: PORTFOLIO_AI_ENABLED, PORTFOLIO_SYNC_ENABLED, TICKER_UNIVERSE_AGGREGATE_ENABLED, SUBSCRIPTIONS_ENABLED. - Gates: /api/analyze, /api/portfolio/sync*, /api/stripe/*, /pricing, ticker_universe writes, portfolio_analysis.analyse(). is_paid_active() returns True for any auth'd user when subscriptions are paused. - Prompts (PROMPT_VERSION 10): universal _COMPLIANCE_RIDER prepended to every system prompt; watch list removed; price-target / close-above-below / trigger / forward-state-as-description rules added; SPECULATIVE pivoted to regime-only scenarios; daily + weekly digests tightened. - Reviewer: deterministic regex/lexicon pre-check fail-closed under the Haiku call; portfolio rider gated by PORTFOLIO_AI_ENABLED; base prompt sharpened for forward-state and MAR forward-opinion patterns; ReviewerVerdict audit table; generate_with_review retry helper. - Migration 0026: purge portfolio_sync + ticker_universe; create reviewer_verdicts. - Copy: MAR cite fixed to Art 3(1)(35) + Art 20 + Del Reg 2016/958; portfolio reframed as browser-only viewer in disclaimer / privacy / terms / about / pricing / landing (en + it). TODO(legal) marker for lawyer sign-off on disclaimer. - Tests: 13 lexicon + 6 reviewer compliance regressions; conftest enables all flags so existing 402 tests still cover their code paths. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
273 lines
10 KiB
HTML
273 lines
10 KiB
HTML
{% extends "public_base.html" %}
|
|
{% block title %}{{ BRAND_NAME }} · Privacy{% endblock %}
|
|
|
|
{% block main %}
|
|
|
|
<section class="public-section">
|
|
<h1 class="public-section__head">Privacy notice</h1>
|
|
<p style="color: var(--muted); font-size: 13px;">
|
|
Last updated: 2026-05-24. The operator (data controller) is
|
|
{{ LEGAL_OPERATOR }}, {{ OPERATOR_JURISDICTION }}. Registered with
|
|
the UK Information Commissioner’s Office under reference
|
|
<strong>ZC098928</strong>. Questions:
|
|
<a href="mailto:{{ OPERATOR_EMAIL }}">{{ OPERATOR_EMAIL }}</a>.
|
|
</p>
|
|
<p>
|
|
This page describes exactly what we collect, what we don’t,
|
|
where it lives, and how long we keep it. It is written from the code,
|
|
not from a template — every claim corresponds to an explicit
|
|
code path we’re happy to point a reviewer at.
|
|
</p>
|
|
</section>
|
|
|
|
<section class="public-section">
|
|
<h2 class="public-section__head">What we collect</h2>
|
|
<ul>
|
|
<li>
|
|
<strong>Your email address</strong>, when you sign in. We use it
|
|
only to send one-time login codes.
|
|
</li>
|
|
<li>
|
|
<strong>An argon2 hash of each login code</strong>, plus expiry
|
|
and attempt counts. The plaintext code is sent to your inbox and
|
|
never written to disk on our side.
|
|
</li>
|
|
<li>
|
|
<strong>A signed session cookie</strong> after you verify a code.
|
|
It contains your user id only and is signed so we can detect
|
|
tampering. Cookie is marked Secure and HttpOnly.
|
|
</li>
|
|
{# Cloud sync + server-side per-ticker aggregate union are flag-gated off.
|
|
See docs/read-markets-compliance-changes.md and app/config.py
|
|
(PORTFOLIO_SYNC_ENABLED, TICKER_UNIVERSE_AGGREGATE_ENABLED). #}
|
|
<li>
|
|
<strong>Anonymised cost ledger</strong> of AI calls (model, tokens,
|
|
cost). No portfolio or personal data is attached to ledger rows.
|
|
</li>
|
|
<li>
|
|
<strong>Referral linkage</strong>: if you signed up via an invite
|
|
link, we record which existing user’s code you used so we can
|
|
apply the agreed referral credit later.
|
|
</li>
|
|
<li>
|
|
<strong>Job-run telemetry</strong>: success/failure timestamps for
|
|
the scheduled jobs that fetch market data and generate AI reads.
|
|
No user identifiers are attached.
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section class="public-section">
|
|
<h2 class="public-section__head">What we don’t collect</h2>
|
|
<ul>
|
|
<li>
|
|
<strong>Your portfolio holdings, in any form, on the server.</strong>
|
|
The portfolio feature is a browser-only composition viewer:
|
|
uploaded CSVs are parsed and returned to your browser, kept in
|
|
<code>localStorage</code>, and never sent back to or stored on
|
|
the server. The server records no per-ticker aggregate of what
|
|
anyone holds.
|
|
</li>
|
|
<li>
|
|
<strong>Third-party analytics or ad cookies.</strong> No Google
|
|
Analytics, no Hotjar, no Segment, no Facebook pixel, no LinkedIn
|
|
tag. (You can verify by viewing-source on any page.)
|
|
</li>
|
|
<li>
|
|
<strong>Browser fingerprints.</strong>
|
|
</li>
|
|
<li>
|
|
<strong>IP-address joins to your user identity.</strong> IP
|
|
addresses are processed transiently by the reverse proxy for
|
|
security and access logging, retained for up to 30 days, and not
|
|
linked to your account record.
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section class="public-section">
|
|
<h2 class="public-section__head">Lawful basis (UK-GDPR Art. 6)</h2>
|
|
<p>We rely on the following lawful bases:</p>
|
|
<ul>
|
|
<li>
|
|
<strong>Performance of a contract</strong> (Art. 6(1)(b)) — for
|
|
operating your account, the sign-in flow, and any paid features.
|
|
</li>
|
|
<li>
|
|
<strong>Legitimate interests</strong> (Art. 6(1)(f)) — for the
|
|
anonymised cost ledger, job-run telemetry, and reverse-proxy access
|
|
logs. Our interest is the secure, abuse-resistant, cost-controlled
|
|
operation of a free public service, balanced against the minimal
|
|
and de-identified nature of the data.
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section class="public-section">
|
|
<h2 class="public-section__head">Automated decisions and profiling</h2>
|
|
<p>
|
|
The Service does not make decisions about you that produce legal or
|
|
similarly significant effects in an automated way (UK-GDPR Art. 22).
|
|
The strategic log and indicator summaries are general editorial
|
|
commentary on public market data, not personalised assessments of
|
|
you, and you remain the sole decision-maker about anything in your
|
|
account.
|
|
</p>
|
|
</section>
|
|
|
|
<section class="public-section">
|
|
<h2 class="public-section__head">Cookies and local storage</h2>
|
|
<ul>
|
|
<li>
|
|
<strong>Session cookie</strong> — strictly necessary for keeping
|
|
you signed in (PECR reg. 6(4)). No prior consent required.
|
|
</li>
|
|
<li>
|
|
<strong>Local preferences</strong> — your chosen theme (light /
|
|
dark) and reading level (Novice / Intermediate) are stored in
|
|
<code>localStorage</code> on your device. They never leave the
|
|
browser.
|
|
</li>
|
|
<li>
|
|
<strong>Local portfolio</strong> — parsed pies live in
|
|
<code>localStorage</code> on your device. They are not sent to
|
|
or stored on the server.
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section class="public-section">
|
|
<h2 class="public-section__head">Where the data lives, and international transfers</h2>
|
|
<p>
|
|
The server runs in {{ OPERATOR_JURISDICTION }}. Data is stored in a
|
|
MariaDB database on the same host, backed up locally.
|
|
</p>
|
|
<p>
|
|
Two flows can take personal data outside the UK:
|
|
</p>
|
|
<ul>
|
|
<li>
|
|
<strong>SMTP</strong> for sending one-time login codes. Operator-hosted,
|
|
currently inside the UK; if that changes we will update this notice.
|
|
</li>
|
|
<li>
|
|
<strong>AI provider calls</strong> for the strategic log and
|
|
indicator summaries. Where the provider sits outside the UK, we
|
|
rely on the UK International Data Transfer Agreement (IDTA) / the
|
|
UK Addendum to the EU Standard Contractual Clauses where no
|
|
adequacy decision applies. Each outbound request carries an
|
|
explicit no-training opt-out header
|
|
(<code>X-OR-Allow-Training: false</code> on OpenRouter); see the
|
|
Third parties section below for the caveats. None of these
|
|
outbound requests contain user holdings or other portfolio data.
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section class="public-section">
|
|
<h2 class="public-section__head">Retention</h2>
|
|
<ul>
|
|
<li>
|
|
<strong>Login codes</strong>: expire after a few minutes; row
|
|
remains briefly to enforce single-use, then is purged.
|
|
</li>
|
|
<li>
|
|
<strong>Session cookies</strong>: expire automatically; you can
|
|
sign out at any time to revoke.
|
|
</li>
|
|
<li>
|
|
<strong>Account</strong>: held until you ask us to delete it.
|
|
Email <a href="mailto:{{ OPERATOR_EMAIL }}">{{ OPERATOR_EMAIL }}</a>.
|
|
</li>
|
|
<li>
|
|
<strong>Cost ledger and job telemetry</strong>: retained for
|
|
operational accounting; no personal data attached.
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section class="public-section">
|
|
<h2 class="public-section__head">Third parties</h2>
|
|
<ul>
|
|
<li>
|
|
<strong>SMTP provider</strong>: an operator-hosted Mailu server
|
|
sends the one-time login codes. The provider sees your email
|
|
address and the code body (the code itself).
|
|
</li>
|
|
<li>
|
|
<strong>AI provider(s)</strong>: DeepSeek (primary) with OpenRouter
|
|
as a fallback. They see the prompt for the strategic log and the
|
|
indicator summaries. These prompts contain public market data and
|
|
headlines — never any user holdings or portfolio data.
|
|
<br>
|
|
<strong>No-training opt-out.</strong> Every OpenRouter request
|
|
carries the <code>X-OR-Allow-Training: false</code> header, which
|
|
signals to OpenRouter and any compatible upstream that the prompt
|
|
must not be used to train or improve models. DeepSeek does not
|
|
currently expose a per-request opt-out. We do not control
|
|
retention or training policies on the provider side beyond the
|
|
headers we set — the provider’s own published data policy is
|
|
the binding statement on that point.
|
|
</li>
|
|
<li>
|
|
<strong>Market-data sources</strong>: Yahoo Finance and a small set
|
|
of public RSS feeds. We request prices and headlines; we don’t
|
|
send them any user identifier.
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section class="public-section">
|
|
<h2 class="public-section__head">Your rights (UK-GDPR)</h2>
|
|
<p>You have the right to:</p>
|
|
<ul>
|
|
<li>Ask what personal data we hold about you (Art. 15, right of access).</li>
|
|
<li>Have inaccurate data corrected (Art. 16, rectification).</li>
|
|
<li>Have your account and associated data deleted (Art. 17, erasure).</li>
|
|
<li>Export the data you can recognise (Art. 20, portability): your
|
|
email and your referral linkage.</li>
|
|
<li>Restrict processing (Art. 18).</li>
|
|
<li>Object specifically to processing carried out on the basis of
|
|
legitimate interests (Art. 21), including any direct marketing.</li>
|
|
<li>Withdraw consent at any time where processing is based on
|
|
consent (Art. 7(3)), e.g. by disabling cloud sync.</li>
|
|
<li>Lodge a complaint with the
|
|
<a href="https://ico.org.uk/" target="_blank" rel="noopener">Information Commissioner’s Office</a>
|
|
if you think we’re mishandling your data.</li>
|
|
</ul>
|
|
<p>
|
|
Email <a href="mailto:{{ OPERATOR_EMAIL }}">{{ OPERATOR_EMAIL }}</a> to
|
|
exercise any of these.
|
|
</p>
|
|
</section>
|
|
|
|
<section class="public-section">
|
|
<h2 class="public-section__head">Children</h2>
|
|
<p>
|
|
The Service is not directed at, and is not intended for use by,
|
|
anyone under 18. Do not create an account if you are under 18. If
|
|
you believe a child has provided personal data to us, contact
|
|
<a href="mailto:{{ OPERATOR_EMAIL }}">{{ OPERATOR_EMAIL }}</a> and we
|
|
will delete it.
|
|
</p>
|
|
</section>
|
|
|
|
<section class="public-section">
|
|
<h2 class="public-section__head">Security incidents</h2>
|
|
<p>
|
|
If we discover a personal-data breach likely to result in a risk to
|
|
your rights and freedoms, we will notify the ICO within 72 hours of
|
|
becoming aware of it, as required by UK-GDPR Art. 33, and notify
|
|
affected users without undue delay where Art. 34 requires.
|
|
</p>
|
|
</section>
|
|
|
|
<section class="public-section">
|
|
<h2 class="public-section__head">Changes to this notice</h2>
|
|
<p>
|
|
Material changes will be flagged in-app and dated above. Trivial
|
|
edits (grammar, restructuring) won’t.
|
|
</p>
|
|
</section>
|
|
|
|
{% endblock %}
|