Skip to content

fix(orders): allow authenticated local bursts - #1145

Merged
khaira777 merged 4 commits into
mainfrom
codex/order-write-limits
Oct 15, 2026
Merged

khaira777 merged 4 commits into
mainfrom
codex/order-write-limits

Conversation

@khaira777

@khaira777 khaira777 commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Intent

PR 5 - Correct local order-write throttling (Fixes #1073)
Branch: codex/order-write-limits
Title: fix(orders): allow authenticated local bursts

Requirements:

  • Reuse the existing private-IP classification and settings/auth limiter conventions.
  • Exempt authenticated local/private POS traffic from the order-write and item-cancel burst buckets.
  • Keep the two buckets separate.
  • Add FLO_ORDER_WRITE_RATE_LIMIT_MAX following existing configuration parsing: a valid positive configured limit; otherwise the existing default of 60/minute.
  • Retain public-interface limits, Server App proxy protection, authorization and all PIN/auth attempt limits.
  • Do not trust caller-supplied forwarding headers or bypass unauthenticated traffic.
  • Return the established JSON error envelope on 429.
  • Document the environment variable.

Acceptance: 61 legitimate authenticated local writes succeed; public traffic remains limited; PIN guessing remains limited; unrelated limiter buckets remain independent.

What Changed

  • Exempted authenticated callers on private, loopback, or Tailscale socket addresses (isAllowedPrivateIp(req.socket.remoteAddress)) from orderWriteRateLimit and orderItemCancelRateLimit in main/routes/orders.ts, while keeping the two buckets separate, returning { error: 'Too many requests, please try again later.' } on 429, and continuing to rate-limit unauthenticated or public-IP traffic without trusting caller-supplied forwarding headers.
  • Added resolveOrderWriteRateLimitMax to support configuring the per-minute ceiling via FLO_ORDER_WRITE_RATE_LIMIT_MAX when set to a positive integer (defaulting to 60), and documented the behavior in docs/reference/api.md.
  • Added regression coverage in tests/order-write-rate-limits.test.ts (wired into npm run test:orders-authz) for authenticated local write bursts, public-interface and spoofed-header rate limiting, bucket isolation, and FLO_ORDER_WRITE_RATE_LIMIT_MAX parsing.

Risk Assessment

✅ Low: The change cleanly scopes the private-IP rate-limit exemption on order-write and item-cancel buckets to authenticated socket connections, preserves public and PIN rate limits, returns the standard JSON error envelope on 429, and adds thorough behavioral test coverage.

Testing

Ran npm run test:orders-authz (tests/orders-authz.test.ts and tests/order-write-rate-limits.test.ts), npm run test:script-coverage, npm run docs:check, and npm run build, and drove a live Main API (startServer()) and Server App (startServerApp()) instance in an isolated temp environment to verify 65 local order writes, 62 local item cancellations, public-socket rate limiting with FLO_ORDER_WRITE_RATE_LIMIT_MAX, independent orderWriteRateLimit / orderItemCancelRateLimit / orderReadRateLimit buckets, spoofed forwarding header and unauthenticated loopback rejection, Server App proxy protection, and manager PIN brute-force rate limiting (backend API/docs change only; no UI surface modified).

  • Live validation: ✅ go - 5 of 5 scenarios driven live against the product
Scenario Result Live Evidence
Authenticated local POS staff member sends a burst of 65 POST /api/orders writes and 62 PATCH /api/orders/:id/items/:itemId/cancel cancellations within 1 minute and all succeed without 429 throttling ✅ pass live ~/.no-mistakes/evidence/01M4YHVZYAJSND63DVGYGE1HHJ/live-server-verification.json and npm run test:orders-authz
Public-interface traffic is throttled at FLO_ORDER_WRITE_RATE_LIMIT_MAX (with fallback to 60/min on unset or non-positive values) and returns the JSON error envelope on 429 ✅ pass live ~/.no-mistakes/evidence/01M4YHVZYAJSND63DVGYGE1HHJ/test-orders-authz.log (tests/order-write-rate-limits.test.ts)
Order-write and item-cancel rate-limit buckets remain independent, and unrelated limiters (orderReadRateLimit and Server App proxy) remain independent ✅ pass live ~/.no-mistakes/evidence/01M4YHVZYAJSND63DVGYGE1HHJ/live-server-verification.json and ~/.no-mistakes/evidence/01M4YHVZYAJSND63DVGYGE1HHJ/test-orders-authz.log
Adversarial: Spoofed forwarding headers (X-Forwarded-For, Forwarded, overridden req.ip) on a public socket and unauthenticated loopback traffic cannot bypass order-write rate limiting ✅ pass live ~/.no-mistakes/evidence/01M4YHVZYAJSND63DVGYGE1HHJ/test-orders-authz.log (tests/order-write-rate-limits.test.ts)
Adversarial: Manager PIN brute-force protection remains active on authenticated local POS traffic ✅ pass live ~/.no-mistakes/evidence/01M4YHVZYAJSND63DVGYGE1HHJ/live-server-verification.json and ~/.no-mistakes/evidence/01M4YHVZYAJSND63DVGYGE1HHJ/test-orders-authz.log
Evidence: Live Main API and Server App order-write rate-limit verification summary

{ "localOrderWriteBurst": { "count": 65, "all201": true, "sample61stStatus": 201, "sample65thStatus": 201, "lastOrderId": 65 }, "localItemCancelBurst": { "count": 62, "all200": true, "sample61stStatus": 200, "sample62ndStatus": 200 }, "serverAppProxy": { "unauthenticatedStatus": 401, "authenticatedOrderCreateStatus": 201 }, "pinBruteForceProtection": { "firstFiveWrongPinStatuses": [403, 403, 403, 403, 403], "sixthAttemptStatus": 429, "sixthAttemptBody": { "error": "Too many PIN attempts. Try again in 15 minutes." } }, "unrelatedBuckets": { "orderReadStatusAfterPinLockoutAndWriteBurst": 200, "rateLimitLimitHeaderOnOrderRead": "120" } }

{
  "localOrderWriteBurst": {
    "count": 65,
    "all201": true,
    "sample61stStatus": 201,
    "sample65thStatus": 201,
    "lastOrderId": 65
  },
  "localItemCancelBurst": {
    "count": 62,
    "all200": true,
    "sample61stStatus": 200,
    "sample62ndStatus": 200
  },
  "serverAppProxy": {
    "unauthenticatedStatus": 401,
    "authenticatedOrderCreateStatus": 201
  },
  "pinBruteForceProtection": {
    "firstFiveWrongPinStatuses": [
      403,
      403,
      403,
      403,
      403
    ],
    "sixthAttemptStatus": 429,
    "sixthAttemptBody": {
      "error": "Too many PIN attempts. Try again in 15 minutes."
    }
  },
  "unrelatedBuckets": {
    "orderReadStatusAfterPinLockoutAndWriteBurst": 200,
    "rateLimitLimitHeaderOnOrderRead": "120"
  }
}
Evidence: Order authorization and order-write rate-limit test suite output

> [email protected] test:orders-authz
> node tests/run-electron-node-test.cjs tests/orders-authz.test.ts && node tests/run-electron-node-test.cjs tests/order-write-rate-limits.test.ts

[DB] Opening database at: /var/folders/y_/1ltcxtwj0zd_w1dg9jv4jl580000gn/T/flo-orders-authz-h1yjQJ/flo.db
[DB] Schema: v0 → v113
[DB] Triggering auto-backup before migrating v0 → v113...
[DB] Auto-backup before migrating v0 → v113 created at /var/folders/y_/1ltcxtwj0zd_w1dg9jv4jl580000gn/T/flo-orders-authz-h1yjQJ/backups/flo-backup-2026-10-15T01-19-54-918Z-pre-v0-to-v113.db
[DB] Install defaults loaded; first-run setup pending
[MIGRATION v23] normalized: 0, unparseable: 0
[MIGRATION v23] merged 0 duplicate customer(s)
[MIGRATION v23] verification: 0 customers, 0 still non-E.164
[MIGRATION v24] normalized: 0, unparseable: 0
[MIGRATION v25] backfilled addons for 0 order items (0 unparseable, skipped)
[MIGRATION v30] backfilled 0 order_item(s) still missing a normalized addons snapshot
[MIGRATION v30] Dropped order_items.addons — order_item_addons is now the only place selected addons live.
[MIGRATION v73] normalized 0 merchant template payload(s); 0 already canonical or left untouched
[DB] integrity_check: ok
[DB] foreign_key_check: clean
  ✓ cashier can advance an order to preparing
  ✓ cashier can void an in-progress item with a valid manager PIN
  ✓ cashier void marks the original item voided
  ✓ owner PIN can authorize an in-progress item void
[Orders] Cancel item error: Error: Invalid manager PIN
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:3353:33
    at sqliteTransaction (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/better-sqlite3/lib/methods/transaction.js:65:24)
    at withTxn (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/db.ts:1334:28)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:3228:29
    at Layer.handleRequest (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/layer.js:152:17)
    at next (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/route.js:157:13)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/express-rate-limit/dist/index.cjs:922:9
    at processTicksAndRejections (node:internal/process/task_queues:104:5)
    at async ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/express-rate-limit/dist/index.cjs:879:5 {
  statusCode: 403
}
[API] Internal error: Error: Invalid manager PIN
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:3353:33
    at sqliteTransaction (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/better-sqlite3/lib/methods/transaction.js:65:24)
    at withTxn (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/db.ts:1334:28)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:3228:29
    at Layer.handleRequest (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/layer.js:152:17)
    at next (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/route.js:157:13)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/express-rate-limit/dist/index.cjs:922:9
    at processTicksAndRejections (node:internal/process/task_queues:104:5)
    at async ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/express-rate-limit/dist/index.cjs:879:5 {
  statusCode: 403
}
  ✓ cashier PIN cannot authorize an in-progress item void
  ✓ server can advance any order, including their own
  ✓ server can cancel an order created by another user, given a valid manager PIN
  ✓ audit log records the server as the actor, not the order's original creator
  ✓ audit log records which manager PIN approved the cancellation
  ✓ server can void an item on an order created by another user
  ✓ audit log records the server as the actor for a cross-user item void
  ✓ server can print-kot for an order created by another user
[Orders] Cancel item error: Error: Invalid manager PIN
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:3353:33
    at sqliteTransaction (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/better-sqlite3/lib/methods/transaction.js:65:24)
    at withTxn (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/db.ts:1334:28)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:3228:29
    at Layer.handleRequest (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/layer.js:152:17)
    at next (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/route.js:157:13)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/express-rate-limit/dist/index.cjs:922:9
    at processTicksAndRejections (node:internal/process/task_queues:104:5)
    at async ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/express-rate-limit/dist/index.cjs:879:5 {
  statusCode: 403
}
[API] Internal error: Error: Invalid manager PIN
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:3353:33
    at sqliteTransaction (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/better-sqlite3/lib/methods/transaction.js:65:24)
    at withTxn (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/db.ts:1334:28)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:3228:29
    at Layer.handleRequest (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/layer.js:152:17)
    at next (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/route.js:157:13)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/express-rate-limit/dist/index.cjs:922:9
    at processTicksAndRejections (node:internal/process/task_queues:104:5)
    at async ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/express-rate-limit/dist/index.cjs:879:5 {
  statusCode: 403
}
  ✓ cashier with an invalid manager PIN is denied
  ✓ invalid PIN denial identifies the PIN, not the role
[DB] Database closed
Order-write and item-cancel rate limits
============================================================
[DB] Opening database at: /var/folders/y_/1ltcxtwj0zd_w1dg9jv4jl580000gn/T/flo-order-write-rate-limits-BEihaF/flo.db
[DB] Schema: v0 → v113
[DB] Triggering auto-backup before migrating v0 → v113...
[DB] Auto-backup before migrating v0 → v113 created at /var/folders/y_/1ltcxtwj0zd_w1dg9jv4jl580000gn/T/flo-order-write-rate-limits-BEihaF/backups/flo-backup-2026-10-15T01-19-57-510Z-pre-v0-to-v113.db
[DB] Install defaults loaded; first-run setup pending
[MIGRATION v23] normalized: 0, unparseable: 0
[MIGRATION v23] merged 0 duplicate customer(s)
[MIGRATION v23] verification: 0 customers, 0 still non-E.164
[MIGRATION v24] normalized: 0, unparseable: 0
[MIGRATION v25] backfilled addons for 0 order items (0 unparseable, skipped)
[MIGRATION v30] backfilled 0 order_item(s) still missing a normalized addons snapshot
[MIG

... [6097 bytes truncated] ...

stakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/layer.js:152:17)
    at next (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/route.js:157:13)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/express-rate-limit/dist/index.cjs:922:9 {
  statusCode: 403
}
  ✓ local wrong PIN attempt #1 returns 403
[API] Internal error: Error: Invalid manager PIN
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:2156:33
    at sqliteTransaction (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/better-sqlite3/lib/methods/transaction.js:65:24)
    at withTxn (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/db.ts:1334:28)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:2059:67
    at Layer.handleRequest (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/layer.js:152:17)
    at next (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/route.js:157:13)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/services/authorization.ts:167:5
    at Layer.handleRequest (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/layer.js:152:17)
    at next (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/route.js:157:13)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/express-rate-limit/dist/index.cjs:922:9 {
  statusCode: 403
}
  ✓ local wrong PIN attempt #2 returns 403
[API] Internal error: Error: Invalid manager PIN
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:2156:33
    at sqliteTransaction (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/better-sqlite3/lib/methods/transaction.js:65:24)
    at withTxn (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/db.ts:1334:28)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:2059:67
    at Layer.handleRequest (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/layer.js:152:17)
    at next (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/route.js:157:13)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/services/authorization.ts:167:5
    at Layer.handleRequest (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/layer.js:152:17)
    at next (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/route.js:157:13)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/express-rate-limit/dist/index.cjs:922:9 {
  statusCode: 403
}
  ✓ local wrong PIN attempt #3 returns 403
[API] Internal error: Error: Invalid manager PIN
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:2156:33
    at sqliteTransaction (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/better-sqlite3/lib/methods/transaction.js:65:24)
    at withTxn (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/db.ts:1334:28)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:2059:67
    at Layer.handleRequest (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/layer.js:152:17)
    at next (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/route.js:157:13)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/services/authorization.ts:167:5
    at Layer.handleRequest (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/layer.js:152:17)
    at next (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/route.js:157:13)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/express-rate-limit/dist/index.cjs:922:9 {
  statusCode: 403
}
  ✓ local wrong PIN attempt #4 returns 403
[API] Internal error: Error: Invalid manager PIN
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:2156:33
    at sqliteTransaction (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/better-sqlite3/lib/methods/transaction.js:65:24)
    at withTxn (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/db.ts:1334:28)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:2059:67
    at Layer.handleRequest (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/layer.js:152:17)
    at next (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/route.js:157:13)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/services/authorization.ts:167:5
    at Layer.handleRequest (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/layer.js:152:17)
    at next (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/route.js:157:13)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/express-rate-limit/dist/index.cjs:922:9 {
  statusCode: 403
}
  ✓ local wrong PIN attempt #5 returns 403
[API] Internal error: Error: Too many PIN attempts. Try again in 15 minutes.
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:2139:33
    at sqliteTransaction (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/better-sqlite3/lib/methods/transaction.js:65:24)
    at withTxn (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/db.ts:1334:28)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/routes/orders.ts:2059:67
    at Layer.handleRequest (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/layer.js:152:17)
    at next (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/route.js:157:13)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/main/services/authorization.ts:167:5
    at Layer.handleRequest (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/layer.js:152:17)
    at next (~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/router/lib/route.js:157:13)
    at ~/.no-mistakes/worktrees/de2296f2e6f8/01M4YHVZYAJSND63DVGYGE1HHJ/node_modules/express-rate-limit/dist/index.cjs:922:9 {
  statusCode: 429
}
  ✓ 6th PIN attempt on local traffic is still throttled by PIN limiter (429)
  ✓ PIN limiter error message is preserved
[DB] Database closed

✅ All order-write rate-limit tests passed

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 5 of 5 scenarios driven live against the product
Scenario Result Live Evidence
Authenticated local POS staff member sends a burst of 65 POST /api/orders writes and 62 PATCH /api/orders/:id/items/:itemId/cancel cancellations within 1 minute and all succeed without 429 throttling ✅ pass live ~/.no-mistakes/evidence/01M4YHVZYAJSND63DVGYGE1HHJ/live-server-verification.json and npm run test:orders-authz
Public-interface traffic is throttled at FLO_ORDER_WRITE_RATE_LIMIT_MAX (with fallback to 60/min on unset or non-positive values) and returns the JSON error envelope on 429 ✅ pass live ~/.no-mistakes/evidence/01M4YHVZYAJSND63DVGYGE1HHJ/test-orders-authz.log (tests/order-write-rate-limits.test.ts)
Order-write and item-cancel rate-limit buckets remain independent, and unrelated limiters (orderReadRateLimit and Server App proxy) remain independent ✅ pass live ~/.no-mistakes/evidence/01M4YHVZYAJSND63DVGYGE1HHJ/live-server-verification.json and ~/.no-mistakes/evidence/01M4YHVZYAJSND63DVGYGE1HHJ/test-orders-authz.log
Adversarial: Spoofed forwarding headers (X-Forwarded-For, Forwarded, overridden req.ip) on a public socket and unauthenticated loopback traffic cannot bypass order-write rate limiting ✅ pass live ~/.no-mistakes/evidence/01M4YHVZYAJSND63DVGYGE1HHJ/test-orders-authz.log (tests/order-write-rate-limits.test.ts)
Adversarial: Manager PIN brute-force protection remains active on authenticated local POS traffic ✅ pass live ~/.no-mistakes/evidence/01M4YHVZYAJSND63DVGYGE1HHJ/live-server-verification.json and ~/.no-mistakes/evidence/01M4YHVZYAJSND63DVGYGE1HHJ/test-orders-authz.log
  • npm run test:orders-authz
  • npm run test:script-coverage
  • npm run docs:check
  • npm run build
  • ELECTRON_RUN_AS_NODE=1 ./node_modules/.bin/electron ./node_modules/ts-node/dist/bin.js --transpile-only -P tests/tsconfig.json -e "<live startServer() + startServerApp() burst, proxy, PIN lockout, and read-bucket independence verification>"
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: FreeOpenSourcePOS/FloCafe/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 7a367ef2-154e-4b6c-8369-6fae7959edbb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread tests/order-write-rate-limits.test.ts Fixed
@khaira777

Copy link
Copy Markdown
Contributor Author

@greptileai review

@greptile-apps

greptile-apps Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[High impact] The PR appears safe to merge; no actionable new issues remain.

Summary

Allows authenticated local order-write bursts while keeping public traffic limited.

  • Authenticated local order writes and item cancellations can burst past the per-minute cap.

Both earlier unnumbered findings are fixed: the default-limit test clears the environment variable, and the long test header is gone. The merge-base checks differed, so this review covered the full PR diff. Checks were code-only; no tests were run.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Order write request] --> B{Authenticated user and private socket?}
  B -->|Yes| D[Permission and action checks]
  B -->|No| C{Write bucket has room?}
  C -->|Yes| D
  C -->|No| E[429 JSON error]
  D --> F[Separate PIN limit when required]
Loading

Reviews (4) · Last reviewed commit: "Merge branch 'main' into codex/order-wri..." · Reviewed by Greptile

Comment thread tests/order-write-rate-limits.test.ts
Comment thread tests/order-write-rate-limits.test.ts Outdated
@khaira777

Copy link
Copy Markdown
Contributor Author

@greptileai review

…iting` on the test auth middleware in `tests/order-write-rate-limits.test.ts` lines 151-166) by registering an app-level `expressRateLimit({ windowMs: 60 * 1000, limit: 1000 })` middleware before the test auth handler in `tests/order-write-rate-limits.test.ts`, matching the convention in `tests/cash-sessions.test.ts` and `tests/cash-closures.test.ts` while leaving route-level `orderWriteRateLimit` and `orderItemCancelRateLimit` test assertions intact. Verified with `npm run test:orders-authz`, `npm run test:script-coverage`, `npm run lint`, `npm run build`, `npm run docs:check`, and `git diff --check`
@khaira777
khaira777 force-pushed the codex/order-write-limits branch from f6e1f78 to 115f582 Compare October 15, 2026 01:28
@khaira777

Copy link
Copy Markdown
Contributor Author

@greptileai review

@khaira777

Copy link
Copy Markdown
Contributor Author

@greptileai review

@khaira777
khaira777 merged commit 2068261 into main Oct 15, 2026
24 checks passed
@khaira777
khaira777 deleted the codex/order-write-limits branch October 15, 2026 01:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(orders): provide rate limit configuration and loopback bypass for order writes

2 participants