compliance: flag-gate AI portfolio + cloud sync + Stripe; de-risk prompts; harden reviewer
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>
This commit is contained in:
parent
ee8384f1ba
commit
47dce1a1a4
38 changed files with 1188 additions and 279 deletions
|
|
@ -37,24 +37,9 @@
|
|||
It contains your user id only and is signed so we can detect
|
||||
tampering. Cookie is marked Secure and HttpOnly.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Anonymous ticker universe</strong>: when you upload a
|
||||
portfolio CSV we record which Yahoo tickers appear, with
|
||||
<em>no link</em> to your account. The same row would exist whether
|
||||
any specific user holds the ticker or not — once a ticker is in
|
||||
the universe, the row carries no signal as to whose import added it.
|
||||
</li>
|
||||
<li>
|
||||
<strong>If you opt in to encrypted cloud sync</strong>: an opaque
|
||||
blob of bytes per user. The blob is your portfolio, encrypted in
|
||||
your browser with a PIN you choose, then wrapped a second time on
|
||||
the server with a key only the server holds. We can’t decrypt
|
||||
the blob to plaintext without your PIN, and we can’t recover
|
||||
your PIN if you forget it. By enabling cloud sync you give your
|
||||
consent (UK-GDPR Art. 6(1)(a)) to this processing; you can
|
||||
withdraw consent at any time by disabling sync in Settings, which
|
||||
also removes the server-side blob.
|
||||
</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.
|
||||
|
|
@ -76,10 +61,12 @@
|
|||
<h2 class="public-section__head">What we don’t collect</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Your portfolio holdings as plaintext on the server.</strong>
|
||||
Parsed pies are returned to your browser and kept in
|
||||
<code>localStorage</code>. The server’s view is the anonymous
|
||||
ticker universe described above.
|
||||
<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
|
||||
|
|
@ -104,24 +91,14 @@
|
|||
<ul>
|
||||
<li>
|
||||
<strong>Performance of a contract</strong> (Art. 6(1)(b)) — for
|
||||
operating your account, the sign-in flow, paid features, and the
|
||||
mechanics of encrypted cloud sync.
|
||||
operating your account, the sign-in flow, and any paid features.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Legitimate interests</strong> (Art. 6(1)(f)) — for the
|
||||
anonymous ticker universe, 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>
|
||||
<li>
|
||||
<strong>Consent</strong> (Art. 6(1)(a)) — where you opt in to
|
||||
encrypted cloud sync (and the related caching of a derived
|
||||
encryption key in your browser’s <code>sessionStorage</code>).
|
||||
You can withdraw consent at any time by disabling sync in
|
||||
Settings; the cached key is cleared and the server-side blob is
|
||||
removed.
|
||||
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>
|
||||
|
|
@ -131,9 +108,10 @@
|
|||
<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 AI portfolio analysis is editorial commentary on the holdings
|
||||
you upload; it does not approve, reject or rank you, and you remain
|
||||
the sole decision-maker about anything in your account.
|
||||
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>
|
||||
|
||||
|
|
@ -151,13 +129,9 @@
|
|||
browser.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Local portfolio + cached sync key</strong> — parsed pies
|
||||
live in <code>localStorage</code> on your device. If you enable
|
||||
cloud sync, the derived encryption key is cached in
|
||||
<code>sessionStorage</code> so you don’t have to re-enter
|
||||
your PIN on every navigation. This caching is performed only with
|
||||
your consent (given when you enable sync); it is cleared when you
|
||||
close the tab or disable sync.
|
||||
<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>
|
||||
|
|
@ -177,14 +151,15 @@
|
|||
currently inside the UK; if that changes we will update this notice.
|
||||
</li>
|
||||
<li>
|
||||
<strong>AI provider calls</strong> for the strategic log, indicator
|
||||
summaries, and (paid) portfolio analysis. 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
|
||||
<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.
|
||||
Third parties section below for the caveats. None of these
|
||||
outbound requests contain user holdings or other portfolio data.
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
|
@ -200,15 +175,6 @@
|
|||
<strong>Session cookies</strong>: expire automatically; you can
|
||||
sign out at any time to revoke.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Ticker universe</strong>: rows untouched for 60 days are
|
||||
evicted by a nightly job. Active tickers remain.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Encrypted portfolio blob</strong>: kept until you disable
|
||||
cloud sync (one click in Settings) or delete your account. We hold
|
||||
one row per user; new uploads overwrite the previous blob.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Account</strong>: held until you ask us to delete it.
|
||||
Email <a href="mailto:{{ OPERATOR_EMAIL }}">{{ OPERATOR_EMAIL }}</a>.
|
||||
|
|
@ -230,22 +196,18 @@
|
|||
</li>
|
||||
<li>
|
||||
<strong>AI provider(s)</strong>: DeepSeek (primary) with OpenRouter
|
||||
as a fallback. They see the prompt for the strategic log, the
|
||||
indicator summaries, and the portfolio analysis call — which
|
||||
contains your holdings only when you press
|
||||
“Generate AI analysis” on a paid plan, and only for the
|
||||
duration of that single call. The portfolio analysis output is not
|
||||
persisted on the server.
|
||||
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; if you do not want your
|
||||
holdings to leave our server at all, do not use the AI portfolio
|
||||
analysis feature. 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.
|
||||
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
|
||||
|
|
@ -263,7 +225,7 @@
|
|||
<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, any active encrypted blob, your referral linkage.</li>
|
||||
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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue