Skip to content

Supabase Voting

Michael DeGuzis edited this page Apr 8, 2026 · 2 revisions

Supabase Voting

This page describes the current Supabase setup for Proton Pulse report voting.

Current Model

Right now the plugin uses a public Supabase client key from the frontend and stores votes in public.report_votes.

  • Client file: src/lib/voting.ts
  • Vote table: public.report_votes
  • Aggregate view: public.report_vote_totals
  • Current identity model: hashed voter_id

This is still a frontend-only voting flow. There is no server-side secret in the plugin.

Table Shape

create table if not exists public.report_votes (
  voter_id text not null,
  app_id text not null,
  report_key text not null,
  vote smallint not null,
  voted_at timestamp with time zone not null default now(),
  updated_at timestamp with time zone not null default now(),
  primary key (voter_id, app_id, report_key)
);

create index if not exists idx_report_votes_app
  on public.report_votes (app_id);

Totals View

create or replace view public.report_vote_totals as
select
  app_id,
  report_key,
  coalesce(sum(case when vote = 1 then 1 else 0 end), 0)::integer as upvotes,
  coalesce(sum(case when vote = -1 then 1 else 0 end), 0)::integer as downvotes
from public.report_votes
group by app_id, report_key;

Grants

The public client flow needs:

grant usage on schema public to anon, authenticated;

grant select, insert, update on table public.report_votes to anon, authenticated;
grant select on table public.report_vote_totals to anon, authenticated;

RLS

If RLS is enabled on report_votes, the current flow also needs policies like:

alter table public.report_votes enable row level security;

create policy "public read votes"
on public.report_votes
for select
to anon, authenticated
using (true);

create policy "public insert votes"
on public.report_votes
for insert
to anon, authenticated
with check (true);

create policy "public update votes"
on public.report_votes
for update
to anon, authenticated
using (true)
with check (true);

These policies match the current voter_id browser flow. They are not the final design.

Keys

The plugin uses a Supabase publishable key. That key is safe to embed in the client.

Do not put these in the plugin:

  • service_role
  • sb_secret

Those are server-side secrets and must stay out of the frontend.

Planned Next Step

The better long-term setup is:

  • enable Supabase anonymous auth
  • sign in anonymously from the plugin
  • store user_id uuid
  • use auth.uid() in RLS policies

That gives the app a real anonymous user identity and a cleaner security model than the current local hash.

Clone this wiki locally