Building a Secure Submission Pipeline with Jekyll, Supabase Edge Function, and Postgres Part 3
This section marks the end of the secure submission pipeline breakdown.
Debugging Error 1: Invalid API Key
First error:
Database insert failed.
Invalid API key.
Double check your Supabase anon or service_role API key.
Cause:
The backend write secret was not set correctly.
Fix:
npx supabase secrets set DID_SUPABASE_WRITE_KEY="SECRET_OR_SERVICE_ROLE_KEY"
Lesson:
The browser uses the publishable key.
The Edge Function uses the secret/service write key.
They are not interchangeable.
Debugging Error 2: Permission Denied
After setting the secret key, the next error was:
Database insert failed.
permission denied for table builder_project_submissions
Grant the required privileges to the current role with:
GRANT SELECT, INSERT ON public.builder_project_submissions TO service_role;
42501
Fix:
grant usage on schema public to service_role;
grant select, insert, update
on public.builder_project_submissions
to service_role;
For the future published-project table:
grant select, insert, update
on public.projects_builder
to service_role;
Important:
Do not grant browser/anon direct write access for this MVP.
The Edge Function should perform the server-side insert.
Successful Insert
After fixing the function secret and granting privileges, the form returned:
Project submitted successfully. Your submission is now in review.
The row appeared in:
public.builder_project_submissions
That confirmed the full pipeline:
Jekyll form
↓
JavaScript fetch
↓
Supabase Edge Function
↓
Server-side validation
↓
Postgres insert
↓
Submission review queue
This was the first working backend ingestion path for the Builder Showcase / ProofMe experiment.
Git Ignore Notes
Keep these tracked:
supabase/functions/submit-builder-project/index.ts
supabase/config.toml
supabase/deno.json
Do not track local secrets or temp files:
.env
*.env
supabase/.env
supabase/.env.*
supabase/.temp/
If a docs folder was accidentally tracked and should be kept locally but removed from Git tracking:
echo "/docs/" >> .gitignore
git rm -r --cached docs/
git add .gitignore
git commit -m "Stop tracking docs folder"
Security Lessons
Key security decisions:
Do not expose service/secret keys in browser JavaScript.
Do not put database passwords in Jekyll files.
Keep RLS enabled.
Do not create public insert policies casually.
Use Edge Function as the write gate.
Validate payloads server-side.
Use a honeypot field.
Use max-length checks.
Use GitHub URL validation.
Use CORS allowlist, but do not treat CORS as full security.
The publishable key is allowed in the browser, but it is not a full security barrier. Anyone can inspect browser JavaScript.
The real protection comes from the backend function and database design.
What Worked
Successful proof points:
- Supabase Postgres database connected
- schema created and extended
- notebook
psycopg2insert tested - SQLAlchemy read helper tested
- Supabase CLI worked through
npx - personal access token worked for CLI commands
- Edge Function created
- function deployed
- frontend form sent payload
- Edge Function received payload
- database insert succeeded
- row appeared in Postgres immediately
Next Steps
The next layer is GitHub README automation.
Planned flow:
repo_url submitted
↓
parse GitHub owner / repo / branch / path
↓
fetch README from GitHub API
↓
store readme_markdown
↓
set readme_fetch_status
↓
review stored README directly from database
Additional future work:
- add GitHub README fetch logic
- add project approval workflow into
projects_builder - create admin review queries
- add unit tests
- refactor notebook helpers into
src/ - move frontend Supabase config into
_data/supabase.yml - create diagrams in draw.io
- write architecture blog post
- add first-party dashboard metrics
Final Architecture State
Current working MVP:
DataInsideData™ Static Site
↓
Builder Showcase Submit Form
↓
JavaScript Payload
↓
Supabase Edge Function
↓
Validation + Write Key
↓
Supabase Postgres
↓
builder_project_submissions
↓
Manual Review
This is now a real backend intake system, not just a static page.
References
- Supabase Edge Functions overview: https://supabase.com/docs/guides/functions
- Supabase Edge Functions quickstart: https://supabase.com/docs/guides/functions/quickstart
- Supabase Function configuration: https://supabase.com/docs/guides/functions/function-configuration
- Supabase Function secrets: https://supabase.com/docs/guides/functions/secrets
- Supabase API keys: https://supabase.com/docs/guides/getting-started/api-keys
- Supabase CLI getting started: https://supabase.com/docs/guides/local-development/cli/getting-started
- Supabase CLI reference: https://supabase.com/docs/reference/cli/introduction
Data Inside Data™.
Tech Hands, a Science Mind, and a Heart for Community.