Smakspor

A bilingual platform for finding food trucks, and for operators to publish location, hours and menu from a single mobile panel.

Live in production · pilot phase · June 2026 – present

Built and shipped alone, for Norway's food-truck market — product discovery, architecture, implementation, security hardening, testing and deployment. It is the evidence the rest of my CV rests on.

The Smakspor public map: food trucks placed on a live map of central Oslo, with an open truck showing today's hours.

The public map · smakspor.no

Plan

What a person moves through

Live map
Mapbox, location search over PostGIS geometry
Public profiles
Operator page, menu, week plan
Following
Web Push notifications, transactional email via Resend
Operator panel
Mobile-first publishing of location, hours and menu
Checkout
Stripe Checkout with a signed webhook

Section

What holds it up

Roles
consumer · owner · staff · admin
Authorization
RBAC plus ownership checks; 401 and 403 are different answers
Data boundaries
PostgreSQL Row Level Security across multi-tenant data
Identity
Google OAuth and passwordless email sign-in
Endpoint safety
Validation, rate limiting, idempotency
Critical paths
Playwright end-to-end tests
Delivery
Vercel, production debugging

The questions you were going to ask

These are the five a technical reviewer opens with on a project like this. Here are my answers, so the call can start somewhere more interesting.

Show me how the permissions work.

There are four roles — consumer, owner, staff and admin — and they answer two different questions. Authentication asks whether I know who you are; authorization asks whether this particular row is yours. Conflating them is how a panel ends up showing one operator another operator's orders.

So the check happens twice, at different depths. The application decides what to render and which endpoints to expose. The database decides what may be read at all: Row Level Security policies in PostgreSQL sit under the queries, keyed to the authenticated user, so a request that slips past the application layer still returns nothing. A 401 means I do not know you. A 403 means I know you and the answer is still no.

What happens if the webhook arrives twice?

Nothing the second time. Stripe does not promise exactly-once delivery, it promises at-least-once, so a payment handler that assumes a single call is a handler that eventually double-credits an order.

Two guards. The signature is verified before the body is trusted at all, because an unsigned endpoint that mutates payment state is an open endpoint. Then the handler is idempotent: the event identifier decides whether this is work to do or work already done, and a repeat is acknowledged and dropped rather than replayed.

Why PostGIS rather than storing two floats?

Because the question the product asks is "who is near me, right now", and that is a spatial query, not an arithmetic one. Latitude and longitude in two columns turns every proximity search into a full scan with trigonometry bolted on top, and it cannot be indexed usefully.

PostGIS gives the geometry a real type, a spatial index, and distance operators the planner understands. The live map and the location search run on the same data the operator panel writes.

How does someone sign in?

Google OAuth, or a one-time code by email. No password field anywhere, because a password field is a liability I would then have to defend — storage, reset flow, reuse, leak response — for a product whose users open it to find lunch.

Transactional email goes through Resend; Web Push carries the notifications people actually asked for by following a truck.

What did you test, and what did you not?

Playwright over the paths where failure is expensive: signing in, publishing a location, and checkout. These are end-to-end tests, so they catch the integration breaking, which is where this product actually breaks.

What I do not have is a broad unit suite. That is a real gap and it is on the site's own list of them.

Stack

  • TypeScript
  • React
  • Next.js
  • Supabase
  • PostgreSQL
  • PostGIS
  • Mapbox
  • Stripe
  • Resend
  • Web Push
  • Playwright
  • Vercel

Where it is honest to stop

Smakspor is in pilot. It is not a system under load, it has not been handed to another engineer, and it has never had to survive a migration I did not plan myself. What it does prove is that I can take a problem from nothing to a running product and hold every layer of it in my head — and that I know which parts of it I would not yet defend in front of a team.