You shape what we build
Hemp'in is built in the open, with the people who use it. Tell us what you need, upvote what matters, and watch it ship — the most-wanted ideas jump the queue.
Sign in to propose & vote.
The month went into making the public objects real and then holding them to their word. Companies, places and events are public pages worth sharing; a stranger can add a company, a place, an event or a product without an account and claim the page once they confirm an email; the directory was cut to what the taxonomy can actually list (1,190 → 429); the company page was rebuilt around the four facts most pages have; /products and /stores opened the supply side. Then a full platform audit (145 features checked against code, database and edge functions) and eleven fixes in one day: account erasure runs, the suppression sync works, marketing never mails members, the claim funnel has a door, the Atlas rep tier reads the live registry and the legislation browser follows the facts, edge drift closed, a migration ledger, halted campaigns resume on a re-screened list, message notifications follow the message, rep activation is one audited call, and the verification ladder means what it says. Next: the last audit items (dead code, bot capture), the M2 trust UI, and the pioneer push on a clean list.
Also warm
Just shipped
Optional side-quests (quest_type=side); create = an owned draft you edit directly; the WORK space (/portal/work) is the back-office. Creating an org IS the professional act, so create_my_org auto-activates professional (#166) — it only requires basic identity first, and the modal pre-checks that instead of dead-ending on org:not_professional.
The tour modal and the Journey were two uncoordinated systems racing over the same beats (say-hi-to-BUD, interests) and the tour navigated away at the end. Now the tour only welcomes; the Journey owns the task list.
Phase A complete: schema + RPCs + admin dashboard + member directory + org knowledge pages + verification wheel + request-a-change/dispute + soft geo-gating. Portal-only, no products/payments. (Seeded to 1,190 over the summer, then cut to 429 — see the directory-cut line.)
87-category taxonomy + work_listings schema + RPCs (T1 gate · 5-free/PRO · open taxonomy) #109; WORK Catalog/Services managers #110; org storefront + Marketplace browse #111; inquire_listing → Inbox; the 5-listing paywall wired to the capability #128. Transaction fees = Ring 3.
Nobody enters a card at signup: we grant PRO ourselves (2 months, or a free founding year for the first 500 on annual), then Stripe converts. checkout/portal/webhook edge functions, apply_stripe_subscription entitlement, PRO skips the onboarding grind, the trial→paid conversion + ending banner, and the /pricing page. €3·mo or €30·yr. See PRICING_MODEL.md.
A demand post IS a discussion thread (thread_kind=sourcing, "deals" topic, business-leads channel). Structured intent rides alongside for matching on family+kind, ACTIVE both ways: posting alerts matching suppliers; a matching listing alerts the buyer. Creator gets a notify mute. Browse is LIFE/Discover, posting/managing is WORK.
The hemp-specific moat: certs at org level (20 seeded — EU/USDA Organic, GOTS, GACP, GMP, ISO, HACCP…), COAs/spec sheets at listing level, self-declared → verified. M1 (schema + RPCs + storage) is live. M2 so far: the cockpit modal to declare certifications (self-declared → verified by a superadmin) and, since 2026-09-07, verified ones count for T3 and show on the public page. Still to come: COA upload at listing level and the supplier trust panel.
An org page only earns trust if it looks real: "Autofill from website" seeds logos/favicons on existing orgs via the enrich-url function (#163), bare-domain websites (hempin.org, no scheme) are accepted instead of rejected, and territory countries resolve (#164). 200 orgs in the directory.
The third blocker on the pioneer push: a claimed company page is blank and nothing helps fill it. Import ships first because it is the only thing that CREATES content — paste your website and we draft the page, draft never live, every field editable, the copied text labelled as yours to rewrite. Then /portal/work/:orgKey became a real workspace: what you sell, where you are and what is coming up all render and edit in one place, in the order the public page shows them, and the four separate manager pages were deleted rather than linked. Plain words throughout — no more draft, unlisted, tier or archive.
The Work slot knows which company you are working on, and now you can change it: the launcher header shows the current company and expands inline to the others. The bar label stays "Work" — a destination should not rename itself, and a company name cannot fit a width-capped slot. Switching re-points the tiles, so "Your company" leads where you just chose.
A company with no member still receives things: people ask it to add products, RSVP to its events, send it sourcing leads. Those are held. When somebody claims the company and proves they belong to it, everything held is released at once and waiting for them on day one — and until they do, nobody can read a word of it.
The place to post what you are looking for has been live for months and has never had a single request, because it asked you to create an account first. Now it does not: /sourcing is open to anyone. Pick a product or a service, pick the kind of thing it is, say what and how much, leave an email. Confirm the address and your request is open to suppliers, who see what you are looking for and not who you are. The board only ever shows requests somebody confirmed by email, so it is empty until real demand arrives, and it will never be padded to look busy.
One plant, five streams, seventeen industries. You can now walk the market from the plant itself or from the industry you work in, and land on any of the 87 categories. Every page leads with what people are actually looking for rather than an empty shelf, and when a category has nothing of its own it shows you the chain around it instead: what the same part of the plant becomes, and how many companies we know of nearby. No page ever shows you a zero. We count companies, we never publish who they are.
1,190 active organisations → 429 on 2026-08-24; 764 archived, none deleted, 209 place pages unpublished with them. The rule was not taste: the 87-leaf taxonomy IS the scope, so "does this belong?" became "can any of its products land on a leaf?".
394 of 427 pages are pure identity — logo, website, country, sometimes a city — so the page is built for four facts instead of six statements of absence. The claim moved from 82% down the page to above the fold on a phone.
A stranger can add a product from /products without an account; confirm by email and it lands as a draft on a company page you then own. /stores is the directory of who sells. The claim step used to drop product submissions on the floor — fixed the same week, and the materialiser was locked to service_role after a first draft left it callable by any signed-in user.
T2 Contact-verified was granted by the act of creating a membership: 11 of 11 memberships were "verified", 2 were proven. Now the flag is honoured only on the domain proof (your email domain is the company's website) or a superadmin's audited decision — everyone re-evaluated by the same rule, the founder's own companies included. T3 Credentialed is reachable through the certifications you can actually declare.
Hero → "Where the hemp world meets and works" + BUD floating the planet; LIFE + WORK app-grids; Free / Pro €3·mo (coming soon); the verified-knowledge graph; install + invest ribbons. The public bottom bar now mirrors the logged-in launcher (#108).
Homepage pricing rebuilt honestly (free shows LIFE *and* WORK), the /pricing comparison page, and the no-card framing: FREE forever, struck €30 → FREE Year 1 for founding, "First 2 months FREE" monthly. The CTA is "Try PRO — pay later".
Admin ▸ Marketing gained Audience (every candidate with the reason it is in or out) and Monitor (staged release 25 → 50 → 150, an automatic halt on complaints or bounces, activations). The first send went out on 2026-09-01 to 225 companies with a published address. Since 2026-09-07 a halted campaign resumes on a re-screened list, and a cleared halt judges only the sends after the clear.
The two intro campaigns of 2026-09-02 halted themselves at 12% hard bounce (the list, not the mail: one scraped directory bounces at 11%); re-screened down to 143 and finished 2026-09-07. The push continues on lists the domain-health and source-quality gates have cleaned first. Original plan: Run from the separate marketing session (docs/marketing/). The ask: come post what you need or list what you sell. Invite deep link → /portal/work/requests?post=1.
The PRO trial intent already survives signup; do the same for ?post=1 so a cold WhatsApp link lands on the post form after joining.
Admin ▸ Marketing over Resend: campaigns + templates + automatic unsubscribe tracking (#157), then a clear confirm, live send progress and surfaced errors (#158), the marketing_audience ambiguous-column fix that was the real "Send does nothing" bug (#159), and gradual drip sends — marketing_settings.daily_limit, resume, and a daily pg_cron → drip-campaigns call at 08:00 UTC (#160). CRM contacts only; never platform users.
Admin ▸ People ▸ Prospects. Org-centric and draft-first: a scout IS an organizations row created as a private draft (already hidden by the existing org RLS — no new policy needed); Publish flips it live and unclaimed. Paste a website → the enrich-url function pulls name, logo, favicon, description, contact email; three independent toggles decide whether that email lands on the org, in the CRM, or in the prospection audience. save_prospect(jsonb) writes it all atomically. Design: CRM_SCOUT_PROSPECTING.md.
The old button was a dead click wherever beforeinstallprompt never fires — always on iOS Safari, and when already installed. Now: fire the real prompt when we have one, otherwise a platform-aware walkthrough (iOS / Android / desktop), and hide it once installed.
One upload pipeline for every image the app will hold: the media bucket + the media_assets registry every feature FKs to, a single <MediaUpload> (client-side resize to ~2000px/~1MB, path {kind}/{owner_id}/{uuid}.{ext}), RLS by owner / org-editor / visibility, and storage policies pinning writes to your own folder. Copyright is a hard gate — license NOT NULL plus a rights_affirmed CHECK, enforced in the DB so no caller can skip it. Avatar drift resolved (avatar_url canonical, profile_image_url deprecated, new avatar_media_id FK). user_settings + wants_email() shipped ahead of their UI so lifecycle email has a preference gate from the first send. Post-to-media is a junction table (status_post_media), not a uuid[] — Portal adds the post-side FK in 2a. Orphan cleanup goes through the Storage API (deleteMedia + a nightly media-sweep edge function), because Supabase blocks purging storage from SQL. Unblocks the feed (§2) and events/places (§3/§4).
The recurring black page. autoUpdate PWA + atomic Netlify deploys means a client on the previous service worker can request a hashed route chunk that no longer exists; React lazy() rejected with nothing catching it. Two defenses, no SW behaviour change: a vite:preloadError self-reload in main.tsx and a route-level ErrorBoundary.
position:sticky silently dies under any ancestor with overflow:hidden on one axis — overflow-x:hidden on html/body was the real cause of the broken sticky nav. Use overflow-x: clip. Also dropped the duplicate topbar inbox and fixed the inbox close glitch (#150).
Many lanes run in parallel, so: CLAUDE.md is a lean lane-router, design docs are spec (the why/how) and never status, and status lives in this roadmap only — the Now block is authoritative. Ship your work and update your lane line in the same PR. 10 finished docs archived.
~2 days: Bubblewrap/PWABuilder over the existing PWA + assetlinks.json + a signed AAB + $25 Play Console + store assets. Blocked only by the in-app-purchase decision. See MOBILE_APP_STORES.md.
1–2 weeks: Apple rejects thin web wrappers (guideline 4.2), so it needs Capacitor plus real native capability (APNs push the likeliest), $99/yr + a Mac, a demo account for review, and expect 1–3 review rounds. Decide IAP first — Apple wants 15–30% on digital subscriptions. See MOBILE_APP_STORES.md.
One production Supabase project and no staging, while the pioneer push is about to add real users. Tier 0 (PR → Netlify Deploy Preview) is free and already available; Tier 1 is a stable staging URL; Tier 2 — a second Supabase project — is the one that actually protects prod data, because a shared-DB staging gives zero protection on migrations, RLS and edge functions. See STAGING_AND_SAFETY.md.
KPI cards open the rows they counted (analytics_drilldown; session/visitor/event shapes, sortable + paged), with card and list totals asserted equal. Journeys show name + avatar and the anon-to-user stitch instead of raw UUIDs. Admin CRM closes the outreach-to-signup loop: exact-email member vs a flagged likely that is never counted as converted. Events / Places / Products admin tabs mirror Organizations through superadmin RPCs. Audit corrections: country_code is the Atlas country viewed (not visitor geo), and the real funnel bug was that OAuth success was never recorded at all.
Built BEFORE seeding any events, because a review queue you cannot work is worse than an empty one. The generic objects tab could only flip status, so a harvested event with a wrong date left two options: publish it wrong, or reject a real event. Adds a superadmin edit RPC over every field, bulk multi-select, month-grouped soonest-first listing, upcoming-vs-past as a first-class filter with live counts on both chips (a stale date is the commonest defect in harvested event data), country/kind/source facets driven by real counts, and the source URL one click away on every row — reviewing an event means opening the page it came from. THE BUG IT FIXES: hemp_events_read allows status IN (published, cancelled), so cancelled events are PUBLIC by design — correct for an event that got called off, but it meant the old Archive button, used to reject a junk draft, published it instead. Verified against prod: the anonymous count went 0 to 1 on exactly that transition. Rejecting is now a delete, cancelling is a separate labelled action, and a bulk delete refuses any event people have already signed up to rather than silently cascading their RSVPs away. Events also gained their own city, venue_name and lat/lng, so an expo carries its own location instead of a places row — a hired convention centre is not a hemp place, and /places stays farms, factories, labs and shops.
One superadmin-gated admin_pending_counts() RPC (one round trip, ~14ms cached) returns count + new-since-you-looked for all 11 admin queues, rendered on the desktop sidebar, the collapsed rail, the mobile bottom bar and the More sheet — with More aggregating everything hidden behind it, since a phone shows ~4 of ~20 sections. Extends NavItem hasDot/count and user_section_views rather than adding a parallel system. Admin gold for attention, red for genuine errors only, BUD pink never; only the BUD inbox pulses, because a console where everything blinks teaches you to ignore blinking.
We shipped the same bug twice: internal functions reused by public pages, failing for every real visitor and only visible in production. Fixed as a class — every edge function and all 257 RPCs audited by verify_jwt AND in-body gate, documented and stamped with an AUDIENCE header; the SSRF/redirect/size/timeout controls extracted into a shared publicGuard module with a visitor+IP rate limiter; proven on both known cases (enrich-url-public refactored onto it, plus a new stateless geocode-address-public built to the contract agreed with the Public lane).
Events were being lost on the public site with only a browser-console trace. Replaying the real anonymous payload returns 201, so the everyday path was fine and the statusless failure means the request never reached the server. Since a blocked client cannot report being blocked, loss is now inferred from what arrives: a per-session monotonic client_seq, with analytics_ingest_health() deriving drops from highest-seq-seen minus rows-landed. track() queues failures and retries instead of swallowing them, and two real payload faults (null props, int4 duration overflow) were found by replay and fixed at source. Also dropped an index with zero scans that was costing write work on every page view.
Every link ever shared from the platform unfurled as a bare text card: unfurlers do not run JavaScript, so useSeo was invisible to them, and the shell carried no og:image, og:url, twitter:card or canonical at all. A Netlify Edge Function now intercepts the shareable routes, looks the object up as anon so RLS stays the boundary, and rewrites the head before serving. Fails open on every path. Adding a new shareable type is one registry entry. OG images are generated on demand with zero new dependencies, reusing the deterministic-per-id idea Commerce built for ListingCover.
The palette had AREAS (SECTION_HEX) and proof layers (PROVENANCE, HEMP_INDEX) but no OBJECT layer, so seven card types had drifted into four variants of one teal shell — Place and Company were character-identical down to the icon tile, and they are the two objects most often adjacent, since /companies/:slug renders both. Now SHAPE carries type: a place is a round beacon (a point in the world), a company/listing/event a squared badge. The radius is load-bearing, the hue is not, which is what lets colour be a taste decision rather than a legibility one. Areas and objects are separated by FINISH, NOT HUE: nine hues are already reserved, so six place kinds cannot all dodge them; instead a section is always ONE FLAT hue and an object is always a two-stop gem, a rule that still holds when a seventh kind is added. Every gem ramp travels hue at constant lightness, because a ramp descending into black is shading — the 2004 gloss-button look. PressSurface makes the product respond to touch at all: MOTION.press was defined and imported by NOBODY while 780 transition-colors carried the whole interaction vocabulary, and hover is a mouse idiom that fires on tap and sticks; cards now sink onto a coloured ledge. Teal was doing two jobs across 119 files / ~498 usages — the WORK area hue AND the generic interactive accent — so the split was made BY DEFINITION rather than by sweep: only 20 of those are focus rings, the other 478 are separable only by intent per site, so a teal-* utility now IS the interactive accent and a surface wanting the area hue opts into --accent. The org brand palette is COMPUTED, not eyeballed: 4.5:1 on the #0d1713 stage, min saturation 35, min lightness 45, 30 degrees clear of BUD — it rejects the three failures the scout predicted (navy 1.76:1, maroon 1.82, brown 1.95). Enforcing separation from all nine section hues too was measured and leaves exactly TWO usable colours, both violet, so that one is deliberately relaxed: wayfinding lives in the nav, which keeps its section colour. Atlas is the PROOF, not the exception — CountryCard and ConceptAtlasLens invented hard-offset (square, heavy border, hard diagonal shadow, identifiable with zero colour budget) and it is now a named material anything can opt into, rather than being flattened away.
2026-09-07: 90 verified · 19 degraded · 5 drift · 5 coded-unused · 26 partial or not built, and eleven fixes the same day: the account-erasure sweep exists and runs; the Resend suppression sync works for the first time; marketing can never mail platform members; the claim funnel has a "confirm your email" door and returns you to your claim; the Atlas rep tier reads the live registry, disputes can be filed, the legislation browser mirrors the facts; three edge functions un-drifted; a migration ledger records what prod actually applied; halted campaigns resume on a re-screened list; message notifications follow the message; rep activation is one audited call; the verification ladder means what it says.
The repo could never rebuild prod (454 applied migrations vs 268 files; 80 files dated after the day they ran). A snapshot of prod's applied list, a generator, and a ledger in applied order now live in the repo, with the rule: real apply date in the filename, regenerate the ledger in the same PR.
Advisor sweep 2026-09-07 after the audit fixes: 0 ERROR. Earlier: 2026-08-02: 0 ERROR (pass), 189 WARN of which 185 are the by-design "authenticated/anon can execute a guarded SECURITY DEFINER RPC" pair. Real remaining items: Auth leaked-password protection still disabled (a dashboard toggle), pg_net installed in public, and two intentional always-true INSERT policies (public application + analytics forms).
A supporter can back the work without an account (Stripe-hosted, closed until switched on); approved investors see the hub and the documents; the data room is edited in /admin/documents.
Retrieval is built and loaded: 129 documents / 11,577 chunks in pgvector, hybrid full-text + vector search, every claim cited, quote length capped by each document's rights. Dark for now — superadmin and corpus-access members only, awaiting the model key. This line said "vision" while the corpus was already in production; corrected 2026-09-07.
The asking is free. Sign in to get the answer — your handle lets us reach you either way.