Passa al contenuto principale

Telemetria

Il Contributor Portal mantiene la propria telemetria all'interno del progetto Supabase del portale (xlsjwlmsqonllvtyplml). Il collector OTel usato da playroll-cpp e playroll-ui non è utilizzato qui. Tenere la telemetria dentro Postgres significa che una domanda di debug come "questo utente ha ricevuto l'email di verifica?" è una singola SQL join con auth.users, auth.audit_log_entries e le tabelle di dominio (applications, onboarding_submissions, payout_receiving_methods, contributor_app_links).

Da dove arrivano gli eventi

Due superfici emettono eventi:

  • Browser (surface = 'web')src/lib/telemetry.ts accumula gli eventi in batch e li invia via POST ogni 5 secondi (o tramite navigator.sendBeacon in fase di unload) alla edge function ingest-telemetry. Un session_id viene generato per page-load e condiviso da tutti gli eventi di quel tab.
  • Edge function (surface = 'edge')activate-contributor-app-link scrive osservazioni lato server dello stesso flusso di attivazione.

L'endpoint del browser è volutamente invocabile senza JWT (la landing del Contributor Portal è pubblica). Per compensare, la edge function applica un'allowlist di origin, un rate limit per IP (60/min, 300/ora), una quota per sessione (100 eventi), una allowlist stretta sui nomi degli eventi e uno stripper degli attributi PII.

Catalogo eventi

EventoSurfaceEmesso quando
auth.otp.requestedwebPrima di chiamare signInWithOtp
auth.otp.sentwebsignInWithOtp ha restituito successo
auth.otp.failedwebsignInWithOtp ha restituito un errore
app_link.activate.requestedwebIl browser ha invocato la edge function
app_link.activate.receivededgeLa edge function è entrata nell'handler
app_link.activate.pendingweb/edgeHa restituito pending_activation
app_link.activate.verifiedweb/edgeHa restituito verified
app_link.activate.failedweb/edgeLa edge function ha catturato un errore
payout.receiving_method.submittedwebL'utente ha cliccato salva sul form IBAN
payout.receiving_method.rejectedwebLa validazione client-side è fallita
payout.receiving_method.persistedwebINSERT andato a buon fine
payout.receiving_method.confirmedwebConferma email andata a buon fine
payout.receiving_method.confirm_failedwebErrore nella conferma email
ui.unhandled_errorwebIl listener window.error è scattato
ui.unhandled_rejectionwebIl listener window.unhandledrejection è scattato
ui.error_boundarywebUn error boundary React ha catturato un crash

Gli eventi interni emessi dall'endpoint di ingest stesso (telemetry.rate_limited, telemetry.policy.violation, telemetry.events.dropped) stanno a fianco, utili per individuare abusi.

Gestione PII

Email, IBAN, codice fiscale, data di nascita, indirizzi e nomi completi non devono mai apparire in chiaro negli attrs. Il frontend passa email al logger; il backend la trasforma in email_hash tramite SHA-256 deterministico di lower(trim(email)). Lo stesso hash è calcolabile direttamente in SQL:

select public.telemetry_email_hash('philip.budgen@gmail.com');

Per gli IBAN sono ammessi negli attrs soltanto iban_country (i primi 2 caratteri) e iban_last4.

Query comuni

Questo utente ha innescato di recente qualche azione sul portale?

with hash as (
select public.telemetry_email_hash('user@example.com') as email_hash
)
select occurred_at, event_name, severity, error_code, attrs
from public.telemetry_events, hash
where telemetry_events.email_hash = hash.email_hash
or telemetry_events.auth_user_id = (
select id from auth.users where email = 'user@example.com'
)
order by occurred_at desc
limit 100;

Se il risultato è vuoto, l'utente non ha mai raggiunto codice client strumentato. Se è presente auth.otp.requested senza auth.otp.sent, il client ha avuto un errore prima di raggiungere Supabase Auth. Se sono presenti entrambi ma Supabase Auth non ha confirmation_sent_at, il problema è a valle (Resend SMTP o inbox del destinatario).

Funnel: chi ha superato l'OTP nelle ultime 24h

select event_name, count(*) as count
from public.telemetry_events
where occurred_at > now() - interval '24 hours'
and event_name like 'auth.otp.%'
group by event_name
order by event_name;
select session_id, count(*) as calls
from public.telemetry_events
where event_name = 'app_link.activate.requested'
and occurred_at > now() - interval '24 hours'
group by session_id
having count(*) > 1
order by calls desc;

Le righe con calls > 1 indicano una regressione del fix di dedup.

Retention

public.purge_old_telemetry_events(retention_days integer default 90) cancella le righe vecchie. Schedulala come cron giornaliero dalla dashboard Supabase → Database → Cron jobs.