A trilingual car marketplace for Algeria: rentals, sales and wedding cortèges, with a subscription engine the database enforces.

Overview.
An Algerian automotive marketplace
A client of the Barmjli agency building a national platform for car rental, car sales and wedding cortèges, selling visibility packages to agencies and showrooms across 48 wilayas.
Fleets sit idle, and the market runs on Facebook
Rental agencies and dealers list on social media with no search, no verification and no way to pay for placement. Buyers call around. Nobody can tell a verified agency from a scammer.
A marketplace with paid placement the client can operate alone
Public search across rentals, sales and cortèges, three supplier account types with dashboards, and an admin panel where staff sell packages, toggle perks, verify documents and see every change in an audit log.
Walkthrough.
Rent, buy or sell from one search box
The home page has to serve three intents at once. A single search with a rent, buy, sell toggle sits above city and brand browsing, live listings and the pitch to owners.


- 1Three intents, one inputRent, buy and sell tabs over one search. Category shortcuts cover luxury, sport, SUV, monthly, cheap, supercars, convertibles, wedding cortèges, yachts and motorbikes.
- 2Cities and brandsWilaya cards for Algiers, Oran, Constantine, Annaba, Sétif, Tlemcen, Blida and Batna, plus a brand grid with dedicated brand pages.
- 3Contact in one tapEvery card exposes a call button and a WhatsApp deep link. There is no in-app checkout yet, so the platform's job is to make the handoff instant.
Filters built for how Algerians shop for cars
The search page is dense on purpose: category chips, a brand row, a location filter and a sort. Listings show daily, weekly and monthly prices with free delivery and insurance flags.

- 1Category and brand rowsChips for every category and brand, driven by constants so the same lists feed the home page and the search.
- 2Price tiersPer-day, per-week and per-month pricing on each card, with delivery and insurance badges from the listing record.
- 3Public view, not raw tableResults come from a listings_public view that adds verified badges, pinning and priority weight from the owner's active subscription.
A listing page that answers the phone call before it happens
The car page carries the gallery, the rental rates, the full specification sheet, accepted payment methods, the seller's card and the rental conditions.


- 1Watermarked galleryPhotos are watermarked with the Biggo mark on the owner's device before upload, so scraped images still advertise the platform.
- 2Rates and conditionsDaily, weekly and monthly rates, kilometre limits, deposit and the documents the renter must bring.
- 3Seller cardAgency or showroom name, verified badge, rating, address and a link to their public profile with all their listings.
A second marketplace inside the first
Wedding car cortèges are a real market in Algeria. Collections are curated by agencies, priced per night, with or without a chauffeur, and booked over WhatsApp.


- 1Collections, not carsA collection groups several vehicles, a chauffeur option and a decoration option under one price.
- 2Booking rulesReservation only after contract signature, exterior cleaning included, deposit rules and chauffeur formula, listed on the page.
- 3Same trust layerThe agency card, verified badge and Biggo guarantee reuse the rental components.
Turning idle fleets into listings
Supply comes from rental agencies, private sellers and showrooms. The join page sells the platform to them with zero commission, direct bookings and three packages.


- 1Three account typesRenter agencies, private sellers and showrooms each get their own signup, setup wizard and dashboard. Showrooms activate instantly.
- 2Packages with real limitsEach package caps listings, photos, ads and edits per month. The caps are enforced by database triggers, not by the UI.
- 3Lead captureThe join form writes to a leads table that the admin team works from the dashboard.
Four roles, one build
Sign in leads to a role-specific dashboard: renters manage listings, documents and ads; sellers manage sale listings; showrooms manage inventory; admins run the marketplace.

- 1Renter dashboardStats, listing CRUD with watermarking, document upload for verification, multi-phone profile and a map pin with Leaflet.
- 2Showroom inventoryBulk operations, internal reference and warranty fields, a sold state that archives without deleting, and a public showroom page.
- 3VerificationIdentity and business documents are uploaded and reviewed by admins before the verified badge appears on listings.
An operator's console with an audit trail
The client's staff moderate listings, verify documents, manage accounts, run promotions, edit legal pages and change packages, all with per-person permissions and a full audit log.
- 1Ten permission keysListings, accounts, documents, ads, packages, subscriptions, leads, settings, admins and audit. Stored as JSON on the admin role and enforced in Postgres policies.
- 2Audit log with diffsOne trigger records before and after images for nine admin tables. The viewer shows per-field diffs, paginates server-side and exports to Excel.
- 3Feature matrixVerified badge, premium badge, pinned in search, pinned on home and priority weight, toggled per package with no deploy.
Architecture.
Engineering deep dives.
1. Subscription limits that cannot be bypassed
- Problem
- Package quotas for listings, photos, ads and monthly edits have to hold even against raw API calls with a stolen token.
- Approach
- One source of truth, fn_package_limit(user, key), where NULL means unlimited, plus SECURITY DEFINER triggers on listings and ads that reject inserts over the cap. Monthly edits are counted through a listing_events row written on every update.
- Trade-off
- Database errors are ugly, so the forms pre-check the same limits for a friendly message. Admin ads bypass the cap through an is_admin short-circuit.
2. Grandfathering with zero data migration
- Problem
- Existing accounts must not break the day limits go live.
- Approach
- The rule 'no subscription row means unlimited' is encoded once in fn_package_limit rather than as a backfill. The same absence-as-privilege pattern covers admins with no role row.
- Trade-off
- Absence as privilege is dangerous. It is why the admin role foreign key restricts deletes instead of setting null, which would have silently escalated everyone holding a deleted role.
3. Feature flags as data
- Problem
- Paid perks such as badges, pinning and ranking had to be togglable by the client without a deploy.
- Approach
- A features catalog joined to packages through package_features. The listings_public view computes badge_verified, is_pinned and priority_weight per row from the owner's active subscription, and every public surface reads the view.
- Trade-off
- The view fans out on every public read. Acceptable at current scale, and the first thing to cache when it is not.
4. Granular admin permissions retrofitted onto live RLS
- Problem
- One admin role had full power. The client needed restricted staff accounts without disrupting production.
- Approach
- A permissions JSON on admin_roles, an admin_has_permission function, and fifteen existing policies altered by exact name rather than dropped. A trigger blocks self-role changes and admin grants without the manage_admins key.
- Trade-off
- Navigation gating in the client is only for UX. The database is the gate.
5. A generic audit log from one trigger
- Problem
- The client wanted a billable record of who changed what across nine admin tables.
- Approach
- A single fn_audit AFTER trigger stores before and after row images as JSON, skips no-op updates and tolerates text and UUID primary keys. The table has admin-only read policies and no insert policy at all, so writes only happen through the trigger.
- Trade-off
- Row images grow without bound. There is no retention policy yet.
6. The incident: RLS runs as the querying role
- Problem
- After revoking function execution from the anonymous role for hygiene, the public site showed zero cars. Every listing policy called admin_has_permission, which anonymous readers could no longer execute.
- Approach
- Diagnosed by simulating the anonymous role in SQL, fixed with a targeted migration, and written up as a convention: grants on functions referenced by policies are part of the public API surface.
- Trade-off
- A one-line grant took the site down. Security hygiene needs the same testing as features.
Business model.
- A handful of active listings
- Monthly refresh quota
- Standard placement
- More listings, photos and ads
- Verified and premium badges
- Pinned in search
- Highest caps
- Pinned on home
- Top priority weight
- Rental listings and cortèges
- Documents and verification
- Ads and usage meters
- Sale listings
- Seller dashboard
- Instant activation
- Inventory with bulk ops
- Public showroom page
- Permission-scoped console
- Packages and subscriptions
- Audit log
Results.
Five quoted modules, live in three months
Showroom accounts, packages and subscriptions, feature flags, the admin core, and multi-admin permissions with an audit log, each shipped as a migration-backed release on a live database.
The client runs it without a developer
Packages, perks, legal pages, settings and staff permissions are all data. Turning a badge on for a tier or restricting a staff account needs no deploy.
Supply onboarding is underway
Renter agencies, private sellers and showrooms are onboarding across the first wilayas. Traffic numbers are the client's to share, so this page sticks to what the code shows.
What's next.
Engineering debt I would pay first
Generate typed database bindings so rows stop being any, add smoke tests and CI, split the single i18n file, add a retention policy for the audit log, and move the environment file out of the repository.
Product roadmap
An online payment gateway for packages once a provider is chosen, in-app booking requests instead of WhatsApp handoff, and analytics for suppliers on views and contact clicks.