* feat(mcp): speak spec revision 2026-07-28 (stateless core) Adopt the 2026-07-28 MCP spec revision on the connector endpoint while keeping every handshake-era client (2025-06-18 and earlier) byte-identical: - Accept per-request _meta protocol negotiation (io.modelcontextprotocol/protocolVersion); unsupported versions return UnsupportedProtocolVersionError (-32022) with the supported list. - Implement server/discover (spec MUST): supported revisions, capabilities including the extensions field, identity, instructions, freshness hints. - Decorate results for stateless clients: required resultType, serverInfo in _meta, and CacheableResult ttlMs/cacheScope on tools/list, prompts/list, resources/list, resources/read. - Validate the standard Mcp-Method/Mcp-Name request headers when present (HeaderMismatchError -32020); absence stays accepted. - Declare the ratified MCP Apps extension (io.modelcontextprotocol/ui) in capabilities; the widgets already use the ratified mime type and _meta.ui.resourceUri shape, so no widget changes are needed. - OAuth: include the RFC 9207 iss parameter on every authorization response (success and error) and advertise authorization_response_iss_parameter_supported in RFC 8414 metadata. Resource-not-found already used -32602 and tools/list ordering was already deterministic; both are covered by the new test file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(mcp): Tasks extension (io.modelcontextprotocol/tasks) Durable handles for long-running MCP tool calls, per the official Tasks extension. A client that declares the extension in its per-request capabilities gets a CreateTaskResult (resultType: "task") immediately; the work completes after the response via after() and lands in the new mcp_tasks table for tasks/get polling. Clients that did not declare the extension are never handed a task (spec MUST). - New mcp_tasks table (migration 20260729094000): company-scoped SELECT RLS, service-role-only writes (mirrors pending_operations), 1-hour expiry, status lifecycle CHECK. pg-real coverage included; triaged as excluded in the full-archive backup contract (transient state). - tasks/get (creator-scoped), tasks/cancel (cooperative, working-only flip), tasks/update (ack no-op: no input_required flows yet). - Tool opt-in via shouldRunAsTask predicate; first producer is gnubok_audit_package, the one genuinely long-running blocking call (multi-minute ZIP generation). estimate_only stays synchronous. - Tool failures complete the task with the standard isError envelope, exactly what the synchronous call would have returned; the failed status stays reserved for infrastructure errors. - server/discover and initialize now advertise the tasks extension. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(mcp): creator-only task RLS, enforced expiry sweep, RoPA entry Compliance-swarm follow-ups on the mcp_tasks migration (editing the migration is safe: it has not shipped beyond the ephemeral PR preview): - SELECT RLS tightened from company-wide to auth.uid() = user_id so the DB grant matches the creator-scoped tasks/get contract; task results carry raw tool output (Art. 5(1)(c)). pg test now proves a same-company colleague cannot read the row. - The 1-hour retention is now enforced, not aspirational: createMcpTask opportunistically deletes expired rows on every creation (idx_mcp_tasks_expires), best-effort (Art. 5(1)(e)). - RoPA entry mcp.async_task_handles added to .compliance/ropa.yaml (Art. 30). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(mcp): literal terminal-update payload for the phantom-column guard The conditional spreads in resolveMcpTask made the payload unresolvable for the no-phantom-columns guard (362 > 360 ceiling). A literal payload writing null for absent terminal fields is equivalent here: the terminal transition sets the complete terminal state. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
665 lines
27 KiB
YAML
665 lines
27 KiB
YAML
# Record of Processing Activities (GDPR Art. 30)
|
|
#
|
|
# Each entry documents one processing activity. Field shape follows EDPB
|
|
# Guidelines 9/2022 §3 (controller-side RoPA): purposes, lawful basis,
|
|
# data subject + data category, recipients, transfer mechanism, retention,
|
|
# security measures. Add an entry whenever a new flow ships that touches
|
|
# personal data: onboarding, integrations, support, or regulator filings.
|
|
|
|
processing_activities:
|
|
|
|
- id: invoices.delivery_history
|
|
name: Leveranshistorik för kundfakturor
|
|
purpose: >-
|
|
Dokumentera att en kundfaktura skickades, när leveransförsöket skedde,
|
|
vilken mottagardomän som användes och vilken exakt PDF som skickades.
|
|
Dashboardens listvy visar endast maskerade mottagardomäner och
|
|
driftmetadata. Fullständigt meddelandeinnehåll stannar på serversidan.
|
|
lawful_basis: art_6_1_b_and_c
|
|
special_category_basis: null
|
|
controller: gnubok-tenant
|
|
processor: supabase_and_resend
|
|
data_subjects:
|
|
- customer_contact
|
|
- company_member
|
|
data_categories:
|
|
- user.contact.email
|
|
- user.communication_content
|
|
- user.activity_timestamp
|
|
- user.document
|
|
recipients:
|
|
- name: Supabase
|
|
country: EU
|
|
role: processor
|
|
- name: Resend
|
|
country: US
|
|
role: processor
|
|
international_transfers:
|
|
applicable: true
|
|
mechanism: scc_2021_c2p
|
|
note: Resend processes the outbound delivery under SCC Module 2.
|
|
retention:
|
|
duration: through_seventh_calendar_year_after_fiscal_year_end
|
|
basis: bfl_7_kap_and_gdpr_storage_limitation
|
|
stored_in:
|
|
- invoice_deliveries
|
|
- document_attachments
|
|
security_measures:
|
|
- active_company_route_context
|
|
- rls_company_scoped
|
|
- masked_recipient_domains_in_list_api
|
|
- no_message_content_in_browser_list_api
|
|
- exact_sent_pdf_worm_protection
|
|
- metadata_only_immutable_audit_log
|
|
- daily_post_retention_pii_redaction
|
|
- signed_provider_delivery_webhook_only
|
|
- masked_recipient_addresses_in_provider_reason_text
|
|
|
|
- id: customer.private_identity
|
|
name: Personnummer för privatkund
|
|
purpose: >-
|
|
Identifiera en privatkund när företagets avtal, fakturering eller
|
|
kundadministration kräver en entydig identitet. Personnumret får endast
|
|
sparas på kundtypen privatkund. Samtliga listnings- och exportytor i API
|
|
och UI exponerar det endast maskerat (fyra sista siffrorna). Undantaget är
|
|
en enskild uppslagsyta (GET /api/customers/{id}/personal-number) som
|
|
returnerar hela värdet för en kund i taget, kräver skrivroll och loggas
|
|
med aktör men aldrig med värdet, så att den som registrerat ett
|
|
personnummer kan kontrollera vad som faktiskt sparats.
|
|
lawful_basis: art_6_1_b
|
|
special_category_basis: null
|
|
controller: gnubok-tenant
|
|
processor: supabase
|
|
data_subjects:
|
|
- customer
|
|
data_categories:
|
|
- user.government_id
|
|
recipients:
|
|
- name: Supabase
|
|
country: EU
|
|
role: processor
|
|
international_transfers:
|
|
applicable: false
|
|
mechanism: null
|
|
note: EU-only processor.
|
|
retention:
|
|
duration: customer_relationship_or_7y_if_accounting_record
|
|
basis: contract_and_bfl_7_kap
|
|
stored_in:
|
|
- customers.personal_number
|
|
- retained_invoice_documents_when_required
|
|
security_measures:
|
|
- aes_256_gcm_field_encryption
|
|
- masked_api_and_ui_output_last4_only
|
|
- write_role_required
|
|
- rls_company_scoped
|
|
# Replaces the previous `no_full_value_read_endpoint`. A single explicit
|
|
# drill-in now exists (GET /api/customers/{id}/personal-number): the
|
|
# value was otherwise write-only, so a user could store a personnummer
|
|
# and never verify what had been stored. Every listing surface still
|
|
# masks; the drill-in requires the write role, returns one customer at a
|
|
# time, and logs actor + customer id without the value.
|
|
- full_value_read_only_via_single_customer_drill_in
|
|
- full_value_read_requires_write_role
|
|
- full_value_read_logged_with_actor_never_value
|
|
|
|
- id: agi.submit
|
|
name: AGI inlämning till Skatteverket
|
|
purpose: >-
|
|
Lämna in lagstadgad arbetsgivardeklaration på individnivå (AGI) till
|
|
Skatteverket varje månad (Skatteförfarandelagen 26 kap.). Innefattar
|
|
huvuduppgift (HU) och individuppgifter (IU) per anställd. Skatteverkets
|
|
kvittens sparas i response_data inklusive signeradAv: personnummer för
|
|
den som BankID-signerade inlämningen. Ändamålet är bevis för vem som
|
|
juridiskt undertecknade deklarationen samt behandlingshistorik; BFL
|
|
5 kap. 6 § och BFNAR 2013:2 kap. 8 kräver att räkenskapsinformationen
|
|
bevarar den faktiska ingivaren, medan själva inlämningen styrs av SFL.
|
|
lawful_basis: art_6_1_c # legal obligation
|
|
special_category_basis: null
|
|
controller: gnubok-tenant
|
|
processor: anthropic-na # software supplier; Skatteverket is recipient, not processor
|
|
data_subjects:
|
|
- employee
|
|
- company_signatory # firmatecknare/deklarationsombud (BankID-signerare av kvittensen)
|
|
data_categories:
|
|
- user.government_id # personnummer (anställda i IU; signerarens i response_data.signeradAv)
|
|
- user.name
|
|
- user.financial.employment # gross salary, tax withheld, benefits
|
|
- user.financial.tax # avgifter, sjuklönekostnad
|
|
recipients:
|
|
- name: Skatteverket
|
|
country: SE
|
|
role: legal_recipient
|
|
international_transfers:
|
|
applicable: false
|
|
mechanism: null
|
|
note: >-
|
|
Sweden-to-Sweden processor-to-public-authority flow. Adequacy decision
|
|
not applicable; no third-country transfer.
|
|
retention:
|
|
duration: 7y
|
|
basis: bfl_7_kap # BFL 7 kap. 2 §: räkenskapsinformation
|
|
stored_in:
|
|
- agi_declarations.xml_content
|
|
- agi_declarations.individuppgifter
|
|
- agi_declarations.response_data # SKV kvittens incl. signeradAv (signerarens pnr); access via company-membership RLS
|
|
- skatteverket_api_audit_log
|
|
security_measures:
|
|
- encryption_at_rest_supabase
|
|
- rls_company_scoped
|
|
- tls_1_3_to_skatteverket
|
|
- bankid_signing_required_for_filing
|
|
- immutable_audit_log
|
|
- personnummer_at_rest_encryption
|
|
|
|
- id: agi.kontrollera.preflight
|
|
name: AGI pre-flight kontrollera (HU/IU)
|
|
purpose: >-
|
|
Skicka enskild HU eller IU som JSON till Skatteverkets kontrollera-
|
|
ändpunkt (v1.7 §7/§8) för dry-run-validering av felmeddelanden och
|
|
STOPP-/AVVISANDE-status innan ett fullständigt underlag laddas upp.
|
|
Skatteverket lagrar inget från denna kontroll; svaret driver vår UI
|
|
som låter användaren rätta felaktigheter i lönekörningen lokalt.
|
|
lawful_basis: art_6_1_c # legal obligation (ancillary to AGI filing)
|
|
special_category_basis: null
|
|
controller: gnubok-tenant
|
|
processor: anthropic-na
|
|
data_subjects:
|
|
- employee
|
|
data_categories:
|
|
- user.government_id # personnummer (BetalningsmottagarId)
|
|
- user.financial.employment # gross salary, tax withheld, benefits
|
|
recipients:
|
|
- name: Skatteverket
|
|
country: SE
|
|
role: legal_recipient
|
|
international_transfers:
|
|
applicable: false
|
|
mechanism: null
|
|
note: Sweden-to-Sweden flow; no third-country transfer.
|
|
retention:
|
|
# Skatteverket retains nothing for kontrollera calls. Our outbound
|
|
# audit row in skatteverket_api_audit_log records who-called-what-when
|
|
# but NOT the payload body, minimising at-rest footprint.
|
|
duration: 7y
|
|
basis: bfl_7_kap
|
|
stored_in:
|
|
- skatteverket_api_audit_log
|
|
security_measures:
|
|
- strict_zod_allow_list_input_validation
|
|
- request_size_cap_64kb
|
|
- rbac_company_member_role_check
|
|
- rls_company_scoped
|
|
- tls_1_3_to_skatteverket
|
|
- immutable_audit_log
|
|
- no_payload_body_persisted
|
|
|
|
- id: moms.declaration
|
|
name: Momsdeklaration till Skatteverket
|
|
purpose: >-
|
|
Lämna in lagstadgad momsdeklaration (utkast, lås och signering) till
|
|
Skatteverkets momsdeklaration-API (Mervärdesskattelagen 17 kap. 24 §).
|
|
lawful_basis: art_6_1_c
|
|
special_category_basis: null
|
|
controller: gnubok-tenant
|
|
processor: anthropic-na
|
|
data_subjects:
|
|
- business_owner
|
|
data_categories:
|
|
- user.government_id # orgnummer (juridiska personer) eller pnr
|
|
- user.financial.tax # rutor 05-62
|
|
recipients:
|
|
- name: Skatteverket
|
|
country: SE
|
|
role: legal_recipient
|
|
international_transfers:
|
|
applicable: false
|
|
mechanism: null
|
|
note: Sweden-to-Sweden flow; no third-country transfer.
|
|
retention:
|
|
duration: 7y
|
|
basis: bfl_7_kap
|
|
stored_in:
|
|
- extension_data (utkast metadata)
|
|
- skatteverket_api_audit_log
|
|
security_measures:
|
|
- rls_company_scoped
|
|
- tls_1_3_to_skatteverket
|
|
- bankid_signing_required_for_filing
|
|
- immutable_audit_log
|
|
|
|
- id: psd2.cash_account_mirror
|
|
name: PSD2-konton speglas till cash_accounts
|
|
purpose: >-
|
|
När en bank-anslutning via Enable Banking returnerar kontolista efter
|
|
lyckad PSD2-consent kopieras kontometadata (IBAN, valuta, kontonamn,
|
|
external_uid) till cash_accounts så att avstämning, motkontoresolver
|
|
och __PRIMARY_SEK__-sentinel kan rutta verifikat utan att läsa JSONB
|
|
från bank_connections.accounts_data vid varje fråga.
|
|
lawful_basis: art_6_1_b # contract (PSD2 consent + bookkeeping service)
|
|
special_category_basis: null
|
|
controller: gnubok-tenant
|
|
processor: anthropic-na
|
|
data_subjects:
|
|
- business_owner
|
|
data_categories:
|
|
- user.financial.bank_account # IBAN
|
|
- user.contact # account name (kontotitel)
|
|
recipients:
|
|
- name: Supabase
|
|
country: EU
|
|
role: processor
|
|
international_transfers:
|
|
applicable: false
|
|
mechanism: null
|
|
note: EU-only processor; no third-country transfer.
|
|
retention:
|
|
duration: consent_lifetime
|
|
basis: psd2_consent
|
|
stored_in:
|
|
- cash_accounts
|
|
- bank_connections.accounts_data
|
|
security_measures:
|
|
- rls_company_scoped
|
|
- data_minimization_no_balance_on_callback
|
|
- failure_emitted_to_event_log
|
|
|
|
- id: transactions.counterparty_iban
|
|
name: Motpartens IBAN på transaktioner
|
|
purpose: >-
|
|
Spara motpartens IBAN på transaktionsraden så att own-account-detector
|
|
kan identifiera överföringar mellan företagets egna konton (1930 → 1932
|
|
etc.) och bokföra båda benen automatiskt istället för att felbokföra
|
|
utflödet som extern kostnad. Krävs även för payment-matchning mot
|
|
leverantörsfakturor.
|
|
lawful_basis: art_6_1_b # contract (bookkeeping service)
|
|
special_category_basis: null
|
|
controller: gnubok-tenant
|
|
processor: anthropic-na
|
|
data_subjects:
|
|
- business_owner
|
|
- counterparty
|
|
data_categories:
|
|
- user.financial.bank_account # counterparty IBAN
|
|
recipients:
|
|
- name: Supabase
|
|
country: EU
|
|
role: processor
|
|
international_transfers:
|
|
applicable: false
|
|
mechanism: null
|
|
note: EU-only processor.
|
|
retention:
|
|
duration: 7y
|
|
basis: bfl_7_kap
|
|
stored_in:
|
|
- transactions.counterparty_iban
|
|
security_measures:
|
|
- rls_company_scoped
|
|
- immutable_after_post
|
|
|
|
- id: skattekonto.drift_alert
|
|
name: Skattekonto-drift via e-post
|
|
purpose: >-
|
|
Underrätta företagets kontaktadress när det cachade Skatteverket-saldot
|
|
avviker från GL 1630 utöver konfigurerad tolerans (> 1 SEK), så
|
|
bokföraren kan granska skattekonto-raderna. E-postmeddelandet
|
|
innehåller ingen finansiell siffra utan en länk till autentiserad
|
|
dashboard; mottagaren valideras mot company_members innan utskick.
|
|
lawful_basis: art_6_1_f # legitimate interest (bookkeeping accuracy)
|
|
special_category_basis: null
|
|
controller: gnubok-tenant
|
|
processor: resend
|
|
data_subjects:
|
|
- business_owner
|
|
- company_member
|
|
data_categories:
|
|
- user.contact.email
|
|
recipients:
|
|
- name: Resend
|
|
country: US
|
|
role: processor
|
|
international_transfers:
|
|
applicable: true
|
|
mechanism: scc_2021_c2p
|
|
note: >-
|
|
Resend (US): SCC Module 2 (controller-to-processor). Outbound
|
|
payload limited to ett notifieringsmail utan finansiella belopp;
|
|
TIA dokumenterad i .compliance/tia/resend.md.
|
|
retention:
|
|
duration: 30d
|
|
basis: event_log_ttl
|
|
stored_in:
|
|
- event_log
|
|
security_measures:
|
|
- recipient_membership_check_before_send
|
|
- no_financial_figures_in_body
|
|
- tls_to_resend
|
|
- rls_company_scoped
|
|
|
|
- id: ai.inference
|
|
name: AI-inferens (kategorisering + dokumenttolkning) via Amazon Bedrock
|
|
purpose: >-
|
|
Föreslå bokföringskategori för banktransaktioner och tolka/extrahera
|
|
uppladdade underlag (kvitton, leverantörsfakturor) med Anthropic
|
|
Claude-modeller körda i Amazon Bedrock. Endast aktiv när tenanten
|
|
uttryckligen aktiverat AI-funktioner: kärntjänsten (bokföring, fakturor,
|
|
moms, rapporter) fungerar fullt ut utan AI.
|
|
lawful_basis: art_6_1_a # consent: opt-in, AI features off by default
|
|
special_category_basis: null
|
|
controller: gnubok-tenant
|
|
processor: aws-bedrock
|
|
data_subjects:
|
|
- business_owner
|
|
- counterparty # namn/belopp på uppladdade underlag
|
|
data_categories:
|
|
- user.financial # transaktionsdata skickad för kategorisering
|
|
- user.document # uppladdade kvitton/fakturor för OCR/extraktion
|
|
recipients:
|
|
- name: Amazon Web Services (Amazon Bedrock)
|
|
country: EU
|
|
role: processor
|
|
international_transfers:
|
|
applicable: false
|
|
mechanism: null
|
|
note: >-
|
|
Inferens körs i AWS-regionen eu-north-1 (Stockholm): ingen överföring
|
|
till tredje land. AWS DPA + SCC + DPF-certifiering finns som
|
|
skyddsmekanism. Prompter lagras ej hos Bedrock efter anropet och används
|
|
ej till modellträning.
|
|
retention:
|
|
duration: none_at_processor
|
|
basis: no_retention_bedrock # prompt not persisted by Bedrock post-inference
|
|
stored_in:
|
|
- ai_usage_tracking # usage metadata only (tokens/cost), no payload body
|
|
security_measures:
|
|
- opt_in_consent_required
|
|
- eu_region_inference_eu_north_1
|
|
- prompts_not_retained_after_inference
|
|
- not_used_for_model_training
|
|
- rls_company_scoped
|
|
- tls_to_bedrock
|
|
|
|
- id: mcp.telemetry
|
|
name: MCP/agent-telemetri i event_log
|
|
purpose: >-
|
|
Varje MCP-verktygsanrop loggar metadata (verktygsnamn, felkod,
|
|
felmeddelande max 500 tecken, latens, aktör, session, frivillig
|
|
distributionskanal-markör, t.ex. 'openclaw' via X-Gnubok-Client-header
|
|
eller ?client=-parameter, sanerad mot allow-list och aldrig använd för
|
|
auktorisation) och varje skill-laddning loggar slug/tier till event_log.
|
|
Syftet är tillförlitlighetsanalys av agentgränssnittet: felfrekvens per
|
|
verktyg, adoption per distributionskanal, korrelation mellan laddade
|
|
skills och efterföljande fel, samt agenters egenrapporterade feedback
|
|
(agent.feedback). Inga verktygsargument eller verktygsresultat
|
|
persisteras.
|
|
lawful_basis: art_6_1_f # legitimate interest (service reliability/improvement)
|
|
special_category_basis: null
|
|
controller: gnubok-tenant
|
|
processor: supabase
|
|
data_subjects:
|
|
- business_owner
|
|
data_categories:
|
|
- user.unique_id # userId, actorId (API-nyckel-id), sessionId
|
|
- user.contact # actorLabel (användarvald API-nyckeletikett)
|
|
recipients:
|
|
- name: Supabase
|
|
country: EU
|
|
role: processor
|
|
international_transfers:
|
|
applicable: false
|
|
mechanism: null
|
|
note: EU-only processor; no third-country transfer.
|
|
retention:
|
|
# Differentiated TTL via /api/events/cleanup/cron: mcp.*/agent.*-rader
|
|
# behålls 180 dagar (felfrekvens-trender kräver mer än leveransfönstret);
|
|
# övriga event_log-rader (leveranshändelser) 30 dagar.
|
|
duration: 180d
|
|
basis: legitimate_interest_reliability
|
|
stored_in:
|
|
- event_log
|
|
security_measures:
|
|
- rls_user_scoped_select # event_log SELECT: auth.uid() = user_id
|
|
- service_role_only_writes
|
|
- error_message_truncated_500_chars
|
|
- no_tool_args_or_results_persisted
|
|
|
|
- id: mcp.company_customer_tools
|
|
name: MCP-verktyg för företagsinställningar och kunder
|
|
purpose: >-
|
|
Låta en uttryckligen behörig användare läsa nödvändiga företags- och
|
|
kunduppgifter samt förbereda ändringar för användarens godkännande.
|
|
Verktygssökningen gnubok_search_tools returnerar endast verktygsmetadata
|
|
och JSON-schema, aldrig företags- eller kundposter. Läsresultat begränsas
|
|
till det aktiva företaget och till verktygets dokumenterade fält.
|
|
lawful_basis: art_6_1_b
|
|
special_category_basis: null
|
|
controller: gnubok-tenant
|
|
processor: supabase
|
|
data_subjects:
|
|
- business_owner
|
|
- company_member
|
|
- customer
|
|
data_categories:
|
|
- user.name
|
|
- user.contact
|
|
- user.financial.bank_account
|
|
recipients:
|
|
- name: Supabase
|
|
country: EU
|
|
role: processor
|
|
international_transfers:
|
|
applicable: false
|
|
mechanism: null
|
|
note: EU-only processor.
|
|
retention:
|
|
duration: pending_operation_lifecycle
|
|
basis: contract_and_user_approval
|
|
stored_in:
|
|
- pending_operations
|
|
security_measures:
|
|
- api_key_scope_companies_read_or_write
|
|
- api_key_scope_customers_read_or_write
|
|
- active_company_filter_on_every_query
|
|
- rls_company_scoped
|
|
- search_returns_tool_metadata_only
|
|
- writes_staged_for_explicit_user_approval
|
|
- plaintext_personnummer_blocked_from_staging
|
|
|
|
- id: mcp.async_task_handles
|
|
name: MCP-uppgiftshandtag för långkörande verktygsanrop
|
|
purpose: >-
|
|
Låta en behörig MCP-klient hämta resultatet av ett långkörande
|
|
verktygsanrop (t.ex. generering av arkivpaket) via ett hållbart
|
|
uppgifts-id i stället för att hålla anslutningen öppen. Raden lagrar
|
|
verktygets svar tills klienten hämtat det; endast den skapande
|
|
användaren kan läsa, och utgångna rader rensas löpande vid nya anrop.
|
|
lawful_basis: art_6_1_b
|
|
special_category_basis: null
|
|
controller: gnubok-tenant
|
|
processor: supabase
|
|
data_subjects:
|
|
- business_owner
|
|
- company_member
|
|
data_categories:
|
|
- user.financial
|
|
- user.activity_timestamp
|
|
recipients:
|
|
- name: Supabase
|
|
country: EU
|
|
role: processor
|
|
international_transfers:
|
|
applicable: false
|
|
mechanism: null
|
|
note: EU-only processor.
|
|
retention:
|
|
duration: 1h
|
|
basis: transient_tool_call_state_storage_limitation
|
|
stored_in:
|
|
- mcp_tasks
|
|
security_measures:
|
|
- rls_user_scoped_select # mcp_tasks SELECT: auth.uid() = user_id
|
|
- service_role_only_writes
|
|
- creator_scoped_task_polling # tasks/get requires the creating user
|
|
- expired_rows_purged_on_creation
|
|
|
|
- id: arsredovisning.signature_evidence
|
|
name: Underskriftsbevis för årsredovisning
|
|
purpose: >-
|
|
Bevara vem som undertecknade en låst årsredovisningsversion, i vilken
|
|
roll, med vilken metod och på vilket datum. Bevisreferensen är endast en
|
|
ogenomskinlig identifierare med prefix archive:, document: eller receipt:.
|
|
Den får inte innehålla namn, e-post, personnummer eller fritext.
|
|
lawful_basis: art_6_1_c
|
|
special_category_basis: null
|
|
controller: gnubok-tenant
|
|
processor: supabase
|
|
data_subjects:
|
|
- company_signatory
|
|
data_categories:
|
|
- user.name
|
|
- user.professional_role
|
|
- user.activity_timestamp
|
|
recipients:
|
|
- name: Supabase
|
|
country: EU
|
|
role: processor
|
|
international_transfers:
|
|
applicable: false
|
|
mechanism: null
|
|
note: EU-only processor.
|
|
retention:
|
|
duration: 7y
|
|
basis: bfl_7_kap_and_arl_signing_evidence
|
|
stored_in:
|
|
- arsredovisning_signature_requests
|
|
- annual_report_versions
|
|
security_measures:
|
|
- rls_company_scoped
|
|
- service_role_only_signature_transitions
|
|
- version_company_period_and_state_validation
|
|
- signed_rows_immutable
|
|
- structured_opaque_evidence_reference
|
|
- future_and_pre_finalization_dates_rejected
|
|
|
|
- id: arsredovisning.bolagsverket.submit
|
|
name: Digital inlämning av årsredovisning till Bolagsverket
|
|
purpose: >-
|
|
Lämna in årsredovisningen (iXBRL) digitalt till Bolagsverkets eget
|
|
utrymme (ÅRL 8 kap.). Avsändarens och undertecknarens personnummer
|
|
krävs av Bolagsverkets API (skapa-inlamningtoken, lamna-in) och
|
|
används endast transient i anropen. Varken personnummer eller hash av
|
|
personnummer sparas i inlämningsposten. Fastställelseintyget signeras
|
|
med e-legitimation hos Bolagsverket, aldrig i appen.
|
|
lawful_basis: art_6_1_c # legal obligation (ÅRL filing duty)
|
|
special_category_basis: null
|
|
controller: gnubok-tenant
|
|
processor: anthropic-na # software supplier; Bolagsverket is recipient, not processor
|
|
data_subjects:
|
|
- business_owner # avsändare/undertecknare (styrelseledamot, VD)
|
|
data_categories:
|
|
- user.government_id # personnummer, endast transient till Bolagsverket
|
|
- user.name # undertecknarens namn
|
|
- user.contact # undertecknarens/kvittens e-post
|
|
- user.financial # årsredovisningens finansiella innehåll
|
|
recipients:
|
|
- name: Bolagsverket
|
|
country: SE
|
|
role: legal_recipient
|
|
international_transfers:
|
|
applicable: false
|
|
mechanism: null
|
|
note: Sweden-to-Sweden flow to a public authority; no third-country transfer.
|
|
retention:
|
|
duration: 7y
|
|
basis: bfl_7_kap # filed .xhtml archived as räkenskapsinformation
|
|
stored_in:
|
|
- arsredovisning_submissions # status, idnummer and checksums, no PNR values or hashes
|
|
- document_attachments # the exact filed iXBRL bytes (WORM)
|
|
security_measures:
|
|
- mtls_client_certificate_to_bolagsverket
|
|
- mtls_key_env_only_injected_from_secret_manager # never in settings/DB; see .env.example custody note
|
|
- pinned_endpoint_allowlist_per_environment # HOSTS map in client.ts; not env-configurable
|
|
- personnummer_and_hash_never_persisted
|
|
- personnummer_never_logged
|
|
- webhook_secret_constant_time_validation
|
|
- rls_company_scoped
|
|
- immutable_status_machine_trigger
|
|
|
|
- id: product.analytics
|
|
name: Produktanalys, sessionsinspelning, felrapportering, enkäter och supportärenden (PostHog)
|
|
purpose: >-
|
|
Förstå hur tjänsten faktiskt används (var användare fastnar i
|
|
onboarding, vilka flöden som avbryts), fånga klientfel och serverfel så
|
|
de kan åtgärdas, och ställa riktade produktfrågor. Endast hostad drift:
|
|
self-hosted-installationer laddar aldrig PostHog (isAnalyticsEnabled()
|
|
kortsluter på NEXT_PUBLIC_SELF_HOSTED, och ingen token bakas in i
|
|
Docker-imagen). Sandbox-/demoföretag identifieras aldrig.
|
|
lawful_basis: art_6_1_f # legitimate interest (produktförbättring och felsökning)
|
|
special_category_basis: null
|
|
controller: gnubok-tenant
|
|
processor: posthog-eu
|
|
data_subjects:
|
|
- business_owner
|
|
- company_member
|
|
data_categories:
|
|
- user.contact.email # person property via identify(), aldrig i event-properties
|
|
- user.name # profiles.full_name
|
|
- user.behavior # sidvisningar, klick, händelser, sessionsinspelning
|
|
- user.device # user agent, skärmstorlek, IP (trunkeras av PostHog)
|
|
# Supportärenden: fritext som användaren SJÄLV skriver till supporten via
|
|
# in-app-dialogen (components/ui/support-link.tsx). Till skillnad från
|
|
# analysdata bär ärendet meddelandetexten, eftersom det är hela poängen
|
|
# med att skriva till supporten. Kan i praktiken innehålla bokförings-
|
|
# detaljer som användaren väljer att beskriva. Widgeten är avstängd:
|
|
# endast vår egen dialog kan öppna ärenden.
|
|
- user.content.support # fritext i supportmeddelanden, användarinitierat
|
|
# EJ organisationsnummer: för enskild firma ÄR orgnr innehavarens
|
|
# personnummer. buildGroupProperties() vägrar skicka det och ett
|
|
# enhetstest låser fast beteendet.
|
|
recipients:
|
|
- name: PostHog
|
|
country: DE
|
|
role: processor
|
|
international_transfers:
|
|
applicable: false
|
|
mechanism: null
|
|
note: >-
|
|
PostHog Cloud EU (Frankfurt). EU-baserat biträde; ingen
|
|
tredjelandsöverföring. DPA tecknat; SCCs gäller för eventuella
|
|
underbiträden utanför EES.
|
|
retention:
|
|
duration: posthog_project_retention
|
|
basis: processor_configured # sätts i PostHog-projektet, ej i denna kodbas
|
|
stored_in:
|
|
- posthog_eu # externt hos biträdet; inget lagras i vår databas
|
|
security_measures:
|
|
- session_replay_masks_all_text # maskTextSelector '*' utöver maskAllInputs
|
|
- session_replay_masks_all_inputs
|
|
- org_number_never_transmitted # låst av test i lib/analytics/__tests__
|
|
- no_pii_in_event_properties # PII endast som person properties via identify()
|
|
# persistence: 'memory' -> ingen analysdata och inga kakor på enheten.
|
|
# TVÅ undantag, båda funktionell UI-state utan personuppgifter, båda
|
|
# skrivna direkt till localStorage av PostHogs egna moduler förbi
|
|
# persistence-inställningen: seenSurvey_<id> ("visa inte igen") och
|
|
# ph_conv_<token> = {"widgetSessionId": uuid} (hitta tillbaka till sitt
|
|
# eget ärende i samma webbläsare). ph_conv skapas vid sidladdning, alltså
|
|
# innan användaren faktiskt kontaktar supporten. Verifierat i prod.
|
|
- no_analytics_data_or_cookies_on_device
|
|
- only_functional_ui_state_persisted # seenSurvey_*, ph_conv_* (uuid, ingen PII)
|
|
# Radering (A.8.10), se .compliance/Data_Classification_Handling.md:
|
|
# ph_conv_* raderas vid utloggning (resetAnalyticsIdentity ->
|
|
# posthog.reset() -> conversations-managerns egen reset). seenSurvey_*
|
|
# har ingen raderingstrigger: ingen PostHog-bundle itererar över
|
|
# localStorage, så SDK:n kan inte hitta nycklarna. Accepterat: värdet är
|
|
# "true" under ett ogenomskinligt survey-id, utan personuppgifter.
|
|
- ph_conv_cleared_on_logout # hindrar delad enhet från att ärva ärendesession
|
|
- same_origin_reverse_proxy # /rl-rewrite; ingen tredjepartsvärd i CSP
|
|
- sandbox_companies_never_identified
|
|
- disabled_entirely_when_self_hosted
|
|
- server_side_payloads_redacted_before_send # lib/observability/redact.ts
|
|
- support_widget_disabled # endast egen UI via conversations-API
|
|
- support_ticket_identity_hmac_signed # POSTHOG_SECRET_API_KEY, serverside
|
|
- support_message_body_never_in_event_properties # breadcrumb bär endast subject
|
|
- email_remains_the_delivery_guarantee # ärendet är ett komplement, ej leveransväg
|