About this page. LocallyGrown is a small platform. We aim to be straightforward about what we have in place, rather than overclaiming. If a common certification or feature isn't listed here, assume we don't have it. If you have a specific security question that isn't answered, reach out via the support channels.

Authentication

  • Session cookies only. Users sign in through the web UI with a username and password; the server issues an auth-session HTTP-only, secure cookie. All authenticated API calls rely on that cookie.
  • No API keys, no OAuth, no personal access tokens. There's no Authorization: Bearer scheme to manage and no OAuth authorize/token endpoints.
  • Password storage. Passwords are hashed with bcrypt before storage; plaintext passwords are never logged or written to disk.
  • Password resets are delivered by signed email links with a limited lifetime; reset tokens are single-use.

Authorization and roles

Authorization is role-based. Six roles exist:

  • Customer
  • Grower
  • Volunteer
  • Manager
  • Admin
  • Superuser

Every request is evaluated against the user's roles and the market it targets — a manager for market A cannot reach data that belongs to market B.

Impersonation

Admins and superusers can act as another user (for example, to help a customer complete an order). Impersonation has several guardrails:

  • Two separate identities on every request. locals.user is the effective user — the person the app is acting as — and drives authorization decisions on the target account. locals.realUser is populated only during an active session and holds the manager at the keyboard; we use it for audit writes and for guards that need to know "who is really doing this."
  • Self-only actions are blocked. A central requireSelfAction guard wraps endpoints that change credentials, payment methods, or account lifecycle state (password change, email change, card add/remove, account deactivation). When an impersonator hits one, the request is rejected with a 403 and an audit row is written — the shopper is not affected.
  • Audit trail. Session starts and stops, every in-session action, every block, and every expiration write a row to the impersonation audit log, including the manager, the shopper, the market, the HTTP method/path, and the outcome (ok, blocked, error). Superusers can search and export the log from the admin UI — see the Acting as a Customer guide for the walkthrough. Session IDs are redacted from the CSV export.
  • Session timeouts. Sessions have a 30-minute sliding inactivity timeout and a 2-hour hard cap, whichever comes first. The expiration event is itself written to the audit log so you can see why a session ended.
  • Soft lock. While an impersonation session is live the shopper's own Deactivate Account button is blocked for the length of the session — once for the manager (because it's a self-only action) and once for the shopper (so two people aren't racing each other on the same account). The session must be ended before the shopper can self-deactivate.
  • Sentry coverage. Blocked self-action attempts and audit-write failures are sent to Sentry so we can spot new misuse patterns or broken code paths quickly — see the integrations guide for Sentry setup.

Payments

  • Stripe handles card data. Card numbers are entered directly into Stripe Elements in the browser and tokenized by Stripe — LocallyGrown never sees the card number. We store only Stripe IDs, the card brand, and the last four digits for display.
  • Each market uses its own Stripe account. Funds flow directly from the customer to the market's Stripe account. LocallyGrown is not an intermediary or custodian of customer funds.
  • PCI scope. Because card data passes through Stripe Elements (SAQ-A scope), LocallyGrown doesn't handle raw cardholder data. See Stripe's security documentation for their compliance posture.
  • Webhooks from Stripe are signature-verified — see the Webhooks guide.

Transport and infrastructure

  • HTTPS everywhere. All production traffic runs over TLS. The application refuses to send auth cookies over plain HTTP.
  • Session cookies are marked HttpOnly, Secure, and SameSite=Lax.
  • Error monitoring runs through Sentry so unexpected server errors are captured and investigated.

Data handling

  • Personal data. Markets hold shopper contact info, order history, and per-market preferences. Shoppers can update their profile, change their password, and deactivate their account from Account Options on the account page.
  • Email unsubscribe from a market email footer deactivates the account (the user is telling us they want to leave the market). Shoppers who just want to pause newsletter email use the Subscribe/Unsubscribe button on their account page instead — see the Shopper Accounts guide.
  • Exports. Admins can export product, order, user, and audit data as CSV for their own records. See the Reports guide.

What we don't claim

We're intentionally precise about this because it affects how you plan around LocallyGrown:

  • No SOC 2 or PCI DSS Level 1 certification held by LocallyGrown itself. We lean on Stripe's PCI posture for card handling.
  • No formal GDPR or HIPAA compliance program, and no signed DPAs by default. If your market has a specific regulatory requirement, get in touch before rolling out.
  • No 24/7 security operations center or phone hotline. Report issues by email and we'll respond as soon as we can.
  • No bug bounty program at this time. We still welcome responsible disclosure — see below.
  • No outbound webhook platform or public developer API. See the API Reference.

Reporting a security issue

If you think you've found a vulnerability, please email [email protected] with a description and reproduction steps, and give us a reasonable window to respond before publishing anything. Thanks in advance — small platforms rely on this.

Related