@volter/twin-gmail 0.0.0-stage → 1.0.1

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.
Files changed (62) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +35 -2
  3. package/dist/generated/pack-facts.json +47 -0
  4. package/dist/journeys/customer-life.json +1013 -0
  5. package/dist/journeys/decisions.json +77 -0
  6. package/dist/journeys/demand.json +43 -0
  7. package/dist/journeys/first-use.json +24 -0
  8. package/dist/journeys/vendor-examples.json +7 -0
  9. package/dist/spec/SOURCE.md +13 -0
  10. package/dist/spec/rfc5321.txt +5323 -0
  11. package/dist/spec/sources.json +25 -0
  12. package/dist/src/cli.d.ts +2 -0
  13. package/dist/src/cli.js +19 -0
  14. package/dist/src/engine/mail.d.ts +19 -0
  15. package/dist/src/engine/mail.js +89 -0
  16. package/dist/src/fetch.d.ts +2 -0
  17. package/dist/src/fetch.js +18 -0
  18. package/dist/src/generated/surface.gen.json +1 -0
  19. package/dist/src/index.d.ts +6 -0
  20. package/dist/src/index.js +11 -0
  21. package/dist/src/manifest.d.ts +2 -0
  22. package/dist/src/manifest.js +68 -0
  23. package/dist/src/screens/home.d.ts +2 -0
  24. package/dist/src/screens/home.js +3 -0
  25. package/dist/src/screens/mail.d.ts +2 -0
  26. package/dist/src/screens/mail.js +142 -0
  27. package/dist/src/semantics/around.d.ts +2 -0
  28. package/dist/src/semantics/around.js +97 -0
  29. package/dist/src/semantics/index.d.ts +2 -0
  30. package/dist/src/semantics/index.js +1 -0
  31. package/dist/src/semantics/shared.d.ts +52 -0
  32. package/dist/src/semantics/shared.js +97 -0
  33. package/dist/src/semantics/states.d.ts +4 -0
  34. package/dist/src/semantics/states.js +33 -0
  35. package/dist/src/server.d.ts +9 -0
  36. package/dist/src/server.js +23 -0
  37. package/dist/src/stream.d.ts +2 -0
  38. package/dist/src/stream.js +10 -0
  39. package/generated/pack-facts.json +47 -0
  40. package/journeys/customer-life.json +1013 -0
  41. package/journeys/decisions.json +77 -0
  42. package/journeys/demand.json +43 -0
  43. package/journeys/first-use.json +24 -0
  44. package/journeys/vendor-examples.json +7 -0
  45. package/package.json +52 -4
  46. package/spec/SOURCE.md +13 -0
  47. package/spec/rfc5321.txt +5323 -0
  48. package/spec/sources.json +25 -0
  49. package/src/cli.ts +20 -0
  50. package/src/engine/mail.ts +76 -0
  51. package/src/fetch.ts +19 -0
  52. package/src/generated/surface.gen.json +1 -0
  53. package/src/index.ts +13 -0
  54. package/src/manifest.ts +86 -0
  55. package/src/screens/home.ts +6 -0
  56. package/src/screens/mail.ts +128 -0
  57. package/src/semantics/around.ts +91 -0
  58. package/src/semantics/index.ts +6 -0
  59. package/src/semantics/shared.ts +111 -0
  60. package/src/semantics/states.ts +41 -0
  61. package/src/server.ts +19 -0
  62. package/src/stream.ts +11 -0
@@ -0,0 +1,77 @@
1
+ {
2
+ "about": "Gmail's receiving server's command decisions: the World's senders (ctx.mail) and RFC 5321 Appendix D; command semantics are the byte-stream front, not HTTP CRUD.",
3
+ "decisions": [
4
+ {
5
+ "operation": "CONNECTION ESTABLISHMENT",
6
+ "demand": "ADR 0032 \u00a74: the World's senders hand gmail.com's mail to this server (world-core smtp.ts sendMail); RFC 5321 Appendix D",
7
+ "mechanism": "handler",
8
+ "why": "The connection front (semantics/around.ts) answers the open with mx.google.com's 220 greeting; the World's senders (ctx.mail from the smtp relay and resend) wait for it before EHLO."
9
+ },
10
+ {
11
+ "operation": "EHLO",
12
+ "demand": "ADR 0032 \u00a74: the World's senders hand gmail.com's mail to this server (world-core smtp.ts sendMail); RFC 5321 Appendix D",
13
+ "mechanism": "handler",
14
+ "why": "The front answers EHLO with the server's keywords (SIZE, 8BITMIME, ENHANCEDSTATUSCODES, PIPELINING, SMTPUTF8; no AUTH or STARTTLS: an MX asks none, and the side port has no certificate); ctx.mail greets with EHLO."
15
+ },
16
+ {
17
+ "operation": "HELO",
18
+ "demand": "ADR 0032 \u00a74: the World's senders hand gmail.com's mail to this server (world-core smtp.ts sendMail); RFC 5321 Appendix D",
19
+ "mechanism": "handler",
20
+ "why": "The front answers HELO as RFC 5321 requires of a server; ctx.mail sends EHLO, an older sender HELO."
21
+ },
22
+ {
23
+ "operation": "MAIL",
24
+ "demand": "ADR 0032 \u00a74: the World's senders hand gmail.com's mail to this server (world-core smtp.ts sendMail); RFC 5321 Appendix D",
25
+ "mechanism": "handler",
26
+ "why": "The front takes the sender (any, as an MX does) and its SIZE, BODY and SMTPUTF8 parameters, refusing a declared SIZE above the 157,286,400 bytes its EHLO advertises with 552 5.3.4, as Google's error page words it; ctx.mail sends MAIL FROM with BODY=8BITMIME for 8-bit messages.",
27
+ "sourceRules": [
28
+ {
29
+ "source": "https://knowledge.workspace.google.com/admin/support/troubleshooting/gmail-smtp-errors-and-codes",
30
+ "means": "552 5.3.4 \"Your message exceeded Google's message size limits.\""
31
+ }
32
+ ]
33
+ },
34
+ {
35
+ "operation": "RCPT",
36
+ "demand": "ADR 0032 \u00a74: the World's senders hand gmail.com's mail to this server (world-core smtp.ts sendMail); RFC 5321 Appendix D",
37
+ "mechanism": "handler",
38
+ "why": "The front accepts a gmail.com address that is a Google account (googleoauth's _person and account, read through ownerReads) and refuses any other at RCPT: 550 5.1.1 for no account, as Google's error page words it; an address at a domain Google does not receive for is refused with 550, unverified, no wording claimed (the page has no row for it, no published transcript of mx.google.com's reply was found, and a session to gmail-smtp-in.l.google.com:25 from themachine on 2026-11-11 timed out after 10 s); ctx.mail turns each reply into the sender's delivered or bounced.",
39
+ "sourceRules": [
40
+ {
41
+ "source": "https://knowledge.workspace.google.com/admin/support/troubleshooting/gmail-smtp-errors-and-codes",
42
+ "means": "550 5.1.1 \"The email account that you tried to reach does not exist.\", and gsmtp added to all errors"
43
+ }
44
+ ]
45
+ },
46
+ {
47
+ "operation": "DATA",
48
+ "demand": "ADR 0032 \u00a74: the World's senders hand gmail.com's mail to this server (world-core smtp.ts sendMail); RFC 5321 Appendix D",
49
+ "mechanism": "handler",
50
+ "why": "The front takes the message and keeps it in each accepted recipient's mailbox (semantics/shared.ts deliver), which the webmail (screens/mail.ts) lists."
51
+ },
52
+ {
53
+ "operation": "RSET",
54
+ "demand": "ADR 0032 \u00a74: the World's senders hand gmail.com's mail to this server (world-core smtp.ts sendMail); RFC 5321 Appendix D",
55
+ "mechanism": "handler",
56
+ "why": "The front clears the transaction as RFC 5321 says."
57
+ },
58
+ {
59
+ "operation": "VRFY",
60
+ "demand": "RFC 5321 Appendix D",
61
+ "mechanism": "handler",
62
+ "why": "The front answers 502 5.5.1, as Gmail's server answers a command it does not take: it verifies no address."
63
+ },
64
+ {
65
+ "operation": "NOOP",
66
+ "demand": "ADR 0032 \u00a74: the World's senders hand gmail.com's mail to this server (world-core smtp.ts sendMail); RFC 5321 Appendix D",
67
+ "mechanism": "handler",
68
+ "why": "The front answers 250 as RFC 5321 says."
69
+ },
70
+ {
71
+ "operation": "QUIT",
72
+ "demand": "ADR 0032 \u00a74: the World's senders hand gmail.com's mail to this server (world-core smtp.ts sendMail); RFC 5321 Appendix D",
73
+ "mechanism": "handler",
74
+ "why": "The front closes the session with 221; ctx.mail ends every transfer with QUIT."
75
+ }
76
+ ]
77
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "measured": "2026-11-07, from ADR 0032 (twin-world docs/adr/0032-mail-is-delivered-to-the-recipients-mailbox-provider.md) and browser-substrate df8efa7e's walks, which read the mail Rallly, Dub, Cal, Twenty and LibreChat send.",
3
+ "applications": [
4
+ {
5
+ "name": "The World's senders",
6
+ "repo": "volter-ai/twin-world",
7
+ "commit": "f242abae30b2",
8
+ "client": "world-core ctx.mail (smtp.ts sendMail): the smtp relay after a client's DATA, resend after an API send",
9
+ "backend": [
10
+ {
11
+ "call": "connect: the 220 greeting",
12
+ "operation": "CONNECTION ESTABLISHMENT",
13
+ "evidence": "packages/world-core/src/smtp.ts transfer"
14
+ },
15
+ {
16
+ "call": "EHLO <sender domain>",
17
+ "operation": "EHLO",
18
+ "evidence": "packages/world-core/src/smtp.ts transfer"
19
+ },
20
+ {
21
+ "call": "MAIL FROM:<sender>, BODY=8BITMIME for 8-bit bytes",
22
+ "operation": "MAIL",
23
+ "evidence": "packages/world-core/src/smtp.ts transfer"
24
+ },
25
+ {
26
+ "call": "RCPT TO:<recipient>, one per gmail.com recipient",
27
+ "operation": "RCPT",
28
+ "evidence": "packages/world-core/src/smtp.ts transfer"
29
+ },
30
+ {
31
+ "call": "DATA, then the sender's message bytes and the end-of-data dot",
32
+ "operation": "DATA",
33
+ "evidence": "packages/world-core/src/smtp.ts transfer"
34
+ },
35
+ {
36
+ "call": "QUIT",
37
+ "operation": "QUIT",
38
+ "evidence": "packages/world-core/src/smtp.ts transfer"
39
+ }
40
+ ]
41
+ }
42
+ ]
43
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "source": "https://www.npmjs.com/package/nodemailer/v/10.0.1",
4
+ "about": "Gmail is the recipient's mailbox, not a service an app integrates: an app reaches it through its sending provider. Maya Okafor makes her Google account at Google's sign-up (googleoauth, which Gmail requires: its accounts are Gmail's mailboxes); Harbourline's Rallly, through the unchanged nodemailer at Rallly's pin (^10.0.1, packages/emails/package.json at 3e63239dbdec), sends a finalized poll's notice through Harbourline's relay (the smtp twin the entry uses, named in twins: a Gmail customer may send this way and need not; the SMTP_HOST, SMTP_PORT and SMTP_SECURE the World injects, no authentication at boot) to the address Maya gave it, maya.okafor+rallly@gmail.com, asserting it accepted and none rejected. The relay hands it to Gmail (twin-world ADR 0032), which keeps it in the account the address tags. The MX host reached directly from an app (gmail-smtp-in.l.google.com) is not this entry: no application integrates it.",
5
+ "files": {
6
+ "package.json": "{\n \"name\": \"gmail-rallly-recipient\",\n \"private\": true,\n \"type\": \"module\",\n \"dependencies\": {\n \"@volter/twin-smtp\": \"1.0.1\",\n \"nodemailer\": \"10.0.1\"\n }\n}\n",
7
+ ".env.example": "SMTP_HOST=\nSMTP_PORT=\nSMTP_SECURE=false\n",
8
+ "deliver.mjs": "import assert from 'node:assert/strict';\nimport { randomBytes } from 'node:crypto';\nimport { writeFileSync } from 'node:fs';\nimport nodemailer from 'nodemailer';\nif (!process.env.VOLTER_WORLD) throw new Error('Run this app through its World.');\n// Maya Okafor makes her Google account at Google's sign-up, as a person does (accounts.google.com/signup); her password\n// is her own, made here and kept in the app's folder for her later sign-in, never a literal\nconst email = 'maya.okafor@gmail.com', tagged = 'maya.okafor+rallly@gmail.com';\nconst password = randomBytes(18).toString('base64url');\nwriteFileSync('.maya-password', password, { mode: 0o600 });\nconst signup = await fetch('https://accounts.google.com/signup', {\n method: 'POST', redirect: 'manual', headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({ email, password, given_name: 'Maya', family_name: 'Okafor', continue: 'https://mail.google.com/mail/u/0/' }),\n});\nassert.ok(/(?:^|[,;]\\s*)SID=[^;]+/.test(signup.headers.get('set-cookie') ?? ''), `sign-up left Maya signed in to Google (status ${signup.status})`);\n// Harbourline's Rallly sends through its own relay (the World's SMTP_HOST and SMTP_PORT) to the address Maya gave it,\n// her tagged one: the relay hands it to Gmail, which keeps it in the account the address tags\nconst transport = nodemailer.createTransport({ host: process.env.SMTP_HOST, port: Number(process.env.SMTP_PORT), secure: process.env.SMTP_SECURE === 'true' });\nconst sent = await transport.sendMail({\n from: 'Rallly <polls@harbourline.be>', to: tagged, subject: 'Your poll has been finalized',\n text: 'The workshop is on Thursday 22 January 2026 at 10:00. Add it to your calendar.',\n});\nassert.deepEqual(sent.accepted, [tagged]);\nassert.deepEqual(sent.rejected, []);\ntransport.close();\nconsole.log(JSON.stringify({ sent: sent.messageId }));\n",
9
+ "read-inbox.mjs": "import assert from 'node:assert/strict';\nimport { readFileSync } from 'node:fs';\nif (!process.env.VOLTER_WORLD) throw new Error('Run this app through its World.');\n// Maya, later, in a browser that holds no Google session: she signs in (accounts.google.com/ServiceLogin) with the\n// webmail as the page to return to, reads her inbox and opens the message Rallly sent her tagged address\nconst email = 'maya.okafor@gmail.com', tagged = 'maya.okafor+rallly@gmail.com';\nconst signin = await fetch('https://accounts.google.com/ServiceLogin', {\n method: 'POST', redirect: 'manual', headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({ email, password: readFileSync('.maya-password', 'utf8'), continue: 'https://mail.google.com/mail/u/0/' }),\n});\nassert.equal(signin.status, 302, 'Google signs Maya in and returns her to her inbox');\nassert.equal(signin.headers.get('location'), 'https://mail.google.com/mail/u/0/');\nconst sid = /(?:^|[,;]\\s*)SID=([^;]+)/.exec(signin.headers.get('set-cookie') ?? '')?.[1];\nassert.ok(sid, 'the sign-in sets her Google session');\nconst cookie = `SID=${sid}`;\nconst inbox = await fetch('https://mail.google.com/mail/u/0/', { headers: { cookie } });\nassert.equal(inbox.status, 200);\nconst listed = await inbox.text();\nassert.match(listed, /Your poll has been finalized/, 'the inbox lists Rallly\\'s message');\nconst id = /href=\"https:\\/\\/mail\\.google\\.com\\/mail\\/u\\/0\\/inbox\\/([^\"]+)\"/.exec(listed)?.[1];\nassert.ok(id, 'the message opens from the inbox');\nconst opened = await fetch(`https://mail.google.com/mail/u/0/inbox/${id}`, { headers: { cookie } });\nassert.equal(opened.status, 200);\nconst page = await opened.text();\nassert.ok(page.includes(tagged), 'the message is the one sent to her tagged address, kept in her own mailbox');\nassert.match(page, /Thursday 22 January 2026 at 10:00/);\nconsole.log(JSON.stringify({ readback: decodeURIComponent(id) }));\n"
10
+ },
11
+ "twins": [
12
+ "smtp"
13
+ ],
14
+ "run": [
15
+ "node",
16
+ "deliver.mjs"
17
+ ],
18
+ "retention": {
19
+ "readback": [
20
+ "node",
21
+ "read-inbox.mjs"
22
+ ]
23
+ }
24
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "vendor": "gmail",
3
+ "author": "Claude (VO-3832)",
4
+ "host": "mail.google.com",
5
+ "about": "Google publishes no SMTP examples for Gmail's MX; the twin's behaviour is cited to its decision rows.",
6
+ "steps": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,54 @@
1
1
  {
2
2
  "name": "@volter/twin-gmail",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.1",
4
+ "description": "Gmail twin: gmail.com's receiving SMTP server for the World's Google accounts, and the mail.google.com webmail where the person reads their mail.",
5
+ "author": "Volter (https://github.com/volter-ai)",
6
+ "license": "Apache-2.0",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/src/index.d.ts",
11
+ "default": "./dist/src/index.js"
12
+ }
13
+ },
14
+ "bin": {
15
+ "world-gmail": "dist/src/cli.js"
16
+ },
17
+ "files": [
18
+ "src",
19
+ "dist",
20
+ "generated",
21
+ "spec",
22
+ "README.md",
23
+ "!**/*.test.ts",
24
+ "journeys/customer-life.json",
25
+ "journeys/vendor-examples.json",
26
+ "journeys/decisions.json",
27
+ "journeys/demand.json",
28
+ "journeys/first-use.json"
29
+ ],
30
+ "peerDependencies": {
31
+ "@volter/twin-googleoauth": "^1.1.2",
32
+ "@volter/world-core": "^3.0.185"
33
+ },
34
+ "engines": {
35
+ "node": ">=22.3"
36
+ },
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/volter-ai/twin-packs-p3.git",
40
+ "directory": "gmail"
41
+ },
42
+ "homepage": "https://github.com/volter-ai/twin-packs-p3/tree/main/gmail#readme",
43
+ "packageManager": "pnpm@12.15.0",
44
+ "devEngines": {
45
+ "packageManager": {
46
+ "name": "pnpm",
47
+ "version": "12.15.0",
48
+ "onFail": "error"
49
+ }
50
+ },
51
+ "volterRelease": {
52
+ "commit": "200f949d661297e45c9ff51b6ae8398bc4acc313"
53
+ }
54
+ }
package/spec/SOURCE.md ADDED
@@ -0,0 +1,13 @@
1
+ # Gmail sources
2
+
3
+ Read 2026-11-07.
4
+
5
+ - `rfc5321.txt`: RFC 5321, Simple Mail Transfer Protocol, October 2008, immutable RFC edition, upstream
6
+ https://www.rfc-editor.org/rfc/rfc5321.txt (the same bytes as the smtp pack's). SHA256:
7
+ `7d560eddff1b213c17d4e1e5f95005728b2f05bc2b3947f0fcc91717c6707634`. It is the command-reply table of
8
+ Gmail's receiving server (`gmail-smtp-in.l.google.com`, the MX `gmail.com` answers), the surface this pack serves on its
9
+ SMTP side port. Gmail's API and IMAP are not served (ADR 0032, later cards).
10
+ - `sources.json` records cited reference pages checked by check-sources; the pack cites the RFC alone.
11
+
12
+ Gmail's replies on that server (`mx.google.com`, `- gsmtp`, `550 5.1.1 The email account that you tried to reach does
13
+ not exist`) are recalled from Gmail's bounces, not read from a published page: the pack says so where it uses them.