@volter/twin-twilio
v0.1.35
Published
Local Twilio twin for the messaging/telephony surface (Messages send + delivery-status lifecycle, Verify start/check, Lookup, IncomingPhoneNumbers, X-Twilio-Signature-signed webhooks) built on @volter/world-core.
Readme
@volter/twin-twilio
Legacy connector helpers: this package still has callable helpers using the retired v1
syncPullAPI. Those paths require migration before use on the current kernel; older helper descriptions below do not establish current compatibility. Check the generated index for protocol standing and use the shared model for current state semantics.
Local Twilio twin for the messaging/telephony surface: Messages send + async delivery-status
lifecycle (queued→sending→sent→delivered, kernel-folded poll-progression, no wall clock),
Verify (start/check with a twin-internal deterministic expected code — no real OTP is ever sent),
Lookup (pure deterministic derivation), IncomingPhoneNumbers list, and
X-Twilio-Signature-signed status-callback/inbound webhooks — built on the shared @volter/world-core
kernel. API-first vendor (no product UI to mirror — the real Twilio surface agents integrate with
is the REST API / twilio SDK; the Twilio Console is a config/analytics dashboard, not an agent
navigation target).
bun packages/twin/twilio/src/cli.tsCoverage
This is a v1 slice of the Twilio API. Three real hosts, disambiguated by host or path shape:
api.twilio.com (2010-04-01 REST — Messages, IncomingPhoneNumbers, Accounts), verify.twilio.com
(Verify v2), lookups.twilio.com (Lookup v2). Requests are application/x-www-form-urlencoded
(not JSON) on api.twilio.com/verify.twilio.com; api.twilio.com responses use a .json path
suffix (verify/lookups do not — grounded live against the installed SDK's own generated route
construction, refining the build spec's blanket ".json suffix" framing to be api-domain-specific).
Errors are Twilio's own {code,message,more_info,status} envelope (NOT fal's {detail}).
Modeled done (33): Messages send + the exact queued→sending→sent→delivered status lifecycle
(kernel-folded, deterministic poll-progression) + get/list + sid-format + isolation +
direction:'outbound-api' + num_segments + the 21604/21211 error-code pair, Accounts fetch
(serving the world's OWN AuthToken — see below), Verify start/check (a twin-internal
deterministic expected code — the RIGHT code approves, the WRONG code stays pending, a genuinely
UNKNOWN to 404s), Lookup (basic + line_type_intelligence + invalid-number 404), a
lazily-seeded IncomingPhoneNumbers list, X-Twilio-Signature sign/verify/tamper + an inbound-
message builder (fresh HMAC-SHA1, distinct from every sibling pack's signing scheme), the exact
Twilio error envelope shape, readOnly/unmodeled-route safety, kernel-persisted state, the
self-referential conformance snapshot, and a list-driven connector pull (idempotent — client.
messages.list() is a genuine Twilio REST list endpoint, unlike fal's handle-only queue).
Left as todo (honest gaps, not yet modeled — 37): MMS media, MessagingServiceSid routing,
scheduled sends, message delete/redact, real cursor pagination, status-callback delivery (an
injected deliverer actually POSTing to a registered URL), Messaging Services/Copilot/short
codes/senders, Voice (Calls/TwiML), Conversations, Verify's email/WhatsApp/call channels, max-
attempts (60202) handling, rate limiting, Fraud Guard, live carrier/CNAM/SIM-swap/reassigned-
number Lookup fields, IncomingPhoneNumbers provision/update, sub-accounts, Usage Records,
Regulatory Compliance Bundles, the full error-code catalog (this pack grounds 6 codes live — real
Twilio has thousands), status-callback retry/fallback-URL, Basic-auth 401 parity and API-Key auth,
connector push, connector pull of verifications (Verify v2 has no bulk list-verifications
endpoint per the live-fetched OpenAPI document — a genuine vendor gap, not an oversight; a
mapVerification pure mapper is written and ready for a future handle-driven pull), and a
fixtures-seed helper.
The deterministic Verify code (D2 honesty)
Verify never sends a real OTP — no SMS/voice/email provider is ever contacted. The "expected
code" is a pure, deterministic function of (serviceSid, to)
(String(parseInt(sha256(serviceSid+':'+to).slice(0,6),16) % 1e6).padStart(6,'0')) that both this
twin and a caller can independently re-derive. The lifecycle semantics are faithful (the right
code approves, the wrong code stays pending), but no real one-time code is ever delivered to a
real phone/inbox.
A kernel-reserved-field gotcha, found in THREE places (not just the one the build spec named)
The kernel's type/id/updatedAt fields are reserved row-meta (packages/world-core/
src/actions.ts) — a resource fields write that includes a literal type key is silently
DROPPED when re-serialized. The build spec called out Twilio Lookup's line_type_intelligence.type
as safe (it's nested, never top-level). Grounding this build against the real SDK/OpenAPI surfaced
two more, real, top-level type collisions the spec didn't name: the Twilio Account resource
(account_enum_type: Trial/Full) and the IncomingPhoneNumber resource (payload.type). This
pack avoids both: accounts.fetch never touches applyTwinWrite on the way out, so its type
field is a plain JSON-literal property that never enters the kernel merge/META-filter pipeline at
all (the credential it reports is read from the signing_key row, which carries no type field). The
IncomingPhoneNumber rows genuinely ARE kernel-persisted (lazily seeded on first list, per root),
so those take the build spec's own prescribed technique: stored as number_type internally,
renamed back to type only in the read path (phoneNumberView()).
The account credential
The world's AccountSid/AuthToken pair is state, not a function of the world's directory: it
is the pack's signing_key resource, persisted through the kernel action log and established
exactly once per root, either way round.
- Seeded via the wire — the real Twilio SDK sends
Authorization: Basic base64(AccountSid:AuthToken)on every call, so an app already configured with a SID/token seeds a virgin world just by talking to it.GET Accounts/{Sid}.jsonthen hands that app back its OWNauth_token, and everyX-Twilio-Signaturethis world produces verifies against the key the app already holds. - Minted with real entropy — nothing presented, so the twin generates a vendor-shaped pair
(
AC+ 32 hex, and a 32-hex AuthToken) the first time it is asked.
Different roots therefore never share a secret — by construction, from entropy rather than from a path hash — and a world COPIED to another directory keeps accepting exactly the credential it had. A pair that is not vendor-shaped is never adopted, and a read-only world is never seeded at all. This twin does not (and never did) REFUSE a request whose Basic pair does not match; adding that 401 is a separate capability with its own verify, not a side effect of moving the secret.
src/twilio-signature.ts holds no credential and reads no state: it is pure HMAC-SHA1 and takes
the AuthToken as an argument (twilioAuthToken(root) is where a caller gets the world's).
X-Twilio-Signature
base64(HMAC-SHA1(AuthToken, URL + sorted-concatenated POST-parameter key+value pairs)) — written
FRESH in src/twilio-signature.ts (never ports fal's ed25519 scheme, replicate/inngest's
HMAC-SHA256, or any sibling pack's signing module). VERBATIM-ported from the actually-installed
[email protected] npm package's own lib/webhooks/webhooks.js getExpectedTwilioSignature — not
just docs. twilio-signature.ts never imports the twin handler, so its 3 signing/builder
capabilities are legitimately mutation-test ALLOW-listed pure crypto.
API-keys/admin surface: sub-account and API-Key/Secret auth management are dashboard/API
surfaces this v1 slice does not model — filed as todo (accounts.subaccounts,
auth.api_key_auth), since a real REST surface exists for them.
See src/twilio-capabilities.ts for the full manifest (the real vendor surface is the
denominator — coverage is honest and partial until the twin reaches it).
