BetaMaShop is in public beta. We improve it continuously, and your feedback shapes what comes next.

Database and security

Your codeEnglish· 4 min read· Updated September 01, 2026

The 53 tables and what is missing from them, row level security everywhere, the four staff roles, and the two views your public site reads.

Your code

Your Supabase project holds 53 tables, created by 30 migration files, with row level security enabled on every one of them and 117 access policies in total.

The migrations live in supabase/migrations/ in your repository. They are applied one by one at publishing, and each one is recorded as soon as it passes, so none is ever replayed.

The tables, by family

FamilyTables
Catalogueproducts, product_variants, product_options, product_option_values, product_attribute_values, attributes, attribute_values, categories, product_categories, collections, product_collections, plus one metadata table per product type: bundle, digital, gift card, service, subscriptions
Stockinventory_locations, inventory_stock, inventory_movements
Commerceorders, order_items, order_status_transitions, customers, customer_segments, customer_segment_members, coupons, coupon_redemptions, product_coupons, reviews
Deliveryshipping_zones, shipping_methods, shipping_carriers
Paymentpayment_providers, payment_transactions, stripe_events
Contentblog_posts, blog_categories, blog_comments, blog_post_tags, blog_post_seo, product_seo, category_seo, marketing_page_seo
Measurementanalytics_events, ab_tests, gsc_connection, gsc_metrics
Team and settingsstaff_users, staff_roles, settings, branding, integrations, integration_connections

All money is stored as whole cents, in bigint columns suffixed _cents. There are no decimal amounts anywhere in the schema.

Demonstration data is deliberately absent: the commerce seed migration is empty. A new project starts with no fake customer, no fake order and no fake coupon.

What the schema does not hold

That table is the whole schema, not a sample of it. There is no invoices table, and no quotes, payments, appointments, bookings, availability or addresses either.

This matters when an agent reads your data. Its read tool carries a list of table names it is allowed to open, and that list is wider than your schema. Ask an agent about your invoices and the read comes back as a database error, not as an answer.

What an agent can actually read is what the families above create: products, orders, customers, stock, coupons, reviews, blog posts. Anything else has to be built first, in a generation that writes the migration and applies it to your project.

Who can do what

Four staff roles are ranked: viewer 1, editor 2, admin 3, owner 4. The function require_staff_role(min_role) raises an error when the caller does not hold at least the rank asked for.

Server side calls made with the service role key run before that check. That is why the key belongs in environment variables and nowhere else.

The first person to sign in becomes owner

The back office signs in against your own Supabase Auth, with email and password or a magic link. There is no sign up form, and MaShop creates no account for you: the first magic link you ask for creates the account.

While no role exists yet, that first signed in account is promoted to owner by the claim_first_owner function. A transaction lock guards it, so two owners cannot appear at once. Everyone else is added from Settings, then Staff.

Publish, then claim your back office. An address that is online and unclaimed is an address anyone who finds it can claim.

What the public can read

The public site never reads your tables. It reads two views: storefront_products and storefront_categories. The read grant is given to anon and authenticated on the views only; the tables stay closed.

storefront_products exposes only products whose status is active, and computes what a page needs: the displayed price, which is the cheapest positive variant or else the base price, the compare at price, the main image, the full gallery, availability and the main category. storefront_categories exposes only categories flagged as published.

The availability rule inside the view is worth knowing before you debug a product that looks out of stock:

  • A product not tracked in stock is always buyable.
  • A tracked product with no variant at all counts as available, because that is missing data rather than a zero.
  • As soon as it has variants, the sum of their stock decides.

Adding a column to a public view

Postgres matches the columns of a replaced view by position, not by name. A column inserted in the middle of storefront_products or storefront_categories makes the migration fail, and that failure stops publishing at the schema step.

This is not theoretical. Between 28 July and 17 August 2026, no newly created project could get its database for exactly that reason. Any new column goes last, always.

File storage

Six storage buckets are created with the schema: product-images public, digital-assets private, category-images, blog-covers, branding-assets, seo-images.

Those buckets live in your Supabase project, like the tables. Deleting a project inside MaShop does not touch them.

databasesecuritysupabaseschemarls
Was this page helpful?
Your feedback is anonymous.