@drawbridge/drawbridge-agents 0.1.37 → 0.1.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -9,6 +9,11 @@ skill) so it stops living only in tribal memory.
|
|
|
9
9
|
|
|
10
10
|
- `OAUTH_TOKEN_HMAC_KEY` must be identical in **drawbridge-api** and **drawbridge-sync** — both
|
|
11
11
|
`assertEnv` it at boot. A mismatch silently breaks realtime token verification.
|
|
12
|
+
- `UNSUBSCRIBE_TOKEN_HMAC_KEY` must be identical in **drawbridge-api** and **drawbridge-sync** —
|
|
13
|
+
both `assertEnv` it at boot. Sync signs the unsubscribe tokens it builds into notification
|
|
14
|
+
emails (`lib/suppression.js buildToken`); api verifies them on the public `/unsubscribe/:token`
|
|
15
|
+
routes (`lib/unsubscribe.js`). A mismatch makes every unsubscribe link 404 as "invalid or
|
|
16
|
+
expired" — one-click unsubscribes silently stop landing, which mail providers punish.
|
|
12
17
|
- reCAPTCHA on auth is a key pair: `GOOGLE_RECAPTCHA_SECRET` (**drawbridge-api**) + `NEXT_PUBLIC_GOOGLE_RECAPTCHA_KEY`
|
|
13
18
|
(**drawbridge-app-web**), both `assertEnv`'d at boot. The api verifies the v3 token app-web's
|
|
14
19
|
`/auth` forms attach and enforces it **unconditionally** on `/oauth/{signup,signin}/request`
|
|
@@ -48,6 +53,19 @@ skill) so it stops living only in tribal memory.
|
|
|
48
53
|
`ads-republishing.<connectionId>`). Never unique-index `coalesce` (superseding requires the
|
|
49
54
|
second insert to land), and writes that set `key` need the writer-side E11000 handler or a
|
|
50
55
|
replayed event logs a false error.
|
|
56
|
+
- **The opt-out floor: `suppression` is the single consent gate** (api ↔ sync ↔ webhooks).
|
|
57
|
+
Writers: api's `/unsubscribe` routes and draw preflight, sync's twilio buffer drain
|
|
58
|
+
(`lib/buffer.js 'twilio.message.inbound'`). The ONLY reader on any send path is sync
|
|
59
|
+
`lib/suppression.js canSend()` — a new lead-facing send path must call it, or opted-out
|
|
60
|
+
recipients get contacted (TCPA/CAN-SPAM exposure). Lead-facing = the notification carries
|
|
61
|
+
`lead`/`workflow`/`connection`; those sends also REQUIRE an org connection (no system-sender
|
|
62
|
+
fallback), while org-less system mail (OTC codes, member invites, security notices) keeps the
|
|
63
|
+
system sender and is never suppressed. Inbound STOP flows webhooks
|
|
64
|
+
(`POST /connection/:id/twilio`, signature-verified against the URL built from
|
|
65
|
+
`APP_CLIENT_WEBHOOKS_URI`) → `buffer` doc → sync drain → platform-global suppression row;
|
|
66
|
+
api registers the number's SmsUrl at twilio connect (`lib/twilio.js`) and `scripts.js
|
|
67
|
+
registerTwilioWebhooks` backfills — a deployed webhooks route with unregistered numbers hears
|
|
68
|
+
nothing, silently.
|
|
51
69
|
- **Adding an email/SMS vendor is a two-repo change** (api ↔ sync). A connection is deliverable
|
|
52
70
|
only if sync implements a sender for its slug — `lib/email.js` (`{ mailchimp, sendgrid }`) and
|
|
53
71
|
`lib/sms.js` (`{ twilio }`), which `queue/notification.js` reads **directly** as its guard and
|
|
@@ -217,6 +235,33 @@ skill) so it stops living only in tribal memory.
|
|
|
217
235
|
drawbridge-utils (15 min). The prize-draw branch is a genuinely different 5 min
|
|
218
236
|
(api `route/otc.js` CODE_TTL_MINUTES) — the two are not interchangeable.
|
|
219
237
|
|
|
238
|
+
## Submission answers are immutable (api ↔ sync ↔ app-web ↔ share)
|
|
239
|
+
|
|
240
|
+
- **Nothing rewrites `submission.fields[]` after the write.** Renaming a field or one of its
|
|
241
|
+
options, and deleting a field, all used to reach back into every stored submission (`$set` on
|
|
242
|
+
`fields.$[element].label` / `.value`, `$pull` by `reference`). An answer is the record of what a
|
|
243
|
+
person was asked and said; a definition change must not touch it. The link forward is
|
|
244
|
+
`fields[].reference` (the field id) and, for choice answers, `fields[].option` (the option id
|
|
245
|
+
captured at submission time) — text is display, ids are identity.
|
|
246
|
+
- **Deleting a field is a tombstone**, `deleted : true`, never a `delete`. Every live read must
|
|
247
|
+
filter `deleted : { $ne : true }` — api's builder field list, create position count, delete
|
|
248
|
+
position decrement, and the share `campaign-fields` cache aggregate; sync's leads exporter and
|
|
249
|
+
the nightly `totals.fields` recount. The **lead-detail endpoint deliberately does not filter**:
|
|
250
|
+
app-web pivots `fieldsById[reference]`, so returning tombstoned definitions is the only thing
|
|
251
|
+
keeping a removed field's historical answers displayable (it labels them `(removed)`).
|
|
252
|
+
- Because the doc survives, **`stream/field.js`'s `delete` handler never fires for a removal** —
|
|
253
|
+
its `update` handler watches `updateDescription.updatedFields.deleted === true` for the
|
|
254
|
+
`campaign.totals.fields` decrement and cache-chain invalidation. A new consumer of field
|
|
255
|
+
deletion must hook the transition, not the delete.
|
|
256
|
+
- **Field validators live in `@drawbridge/drawbridge-utils/fields-validate`**, consumed by BOTH
|
|
257
|
+
drawbridge-share's form and drawbridge-api's submission POST — `yup` is injected so each keeps
|
|
258
|
+
its own copy. A rule added on one side only is client-side theater: the submission endpoint is
|
|
259
|
+
publicly reachable. `select` is checked in-list against the field's own options; `required()`
|
|
260
|
+
honours `attributes.required === false`.
|
|
261
|
+
- **`scripts/backfill-submission-option-ids.js` must run before any further option rename lands.**
|
|
262
|
+
It matches legacy answers to options by text one last time; once a definition and its stored
|
|
263
|
+
text diverge, nothing can match them again.
|
|
264
|
+
|
|
220
265
|
## Growth attribution tags (growth ↔ api)
|
|
221
266
|
|
|
222
267
|
- drawbridge-growth `lib/tags.js` MIRRORS drawbridge-api `lib/utm.js` — allow-listed keys, trim,
|
package/package.json
CHANGED