salamailer 1.0.0

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.
package/LICENSE ADDED
@@ -0,0 +1,43 @@
1
+ Salamailer — licensing
2
+
3
+ This repository contains two differently-licensed bodies of work.
4
+
5
+ 1. CLIENT CODE — MIT licensed
6
+ The following files, and only these files, are released under the MIT
7
+ License set out below. They are the code that runs on the user's own
8
+ machine (the CLI/MCP client and the browser crypto module), and they are
9
+ published so that anyone can read, audit and verify what handles their
10
+ plaintext and keys:
11
+
12
+ cli/salamailer.mjs
13
+ public/js/crypto.js
14
+
15
+ 2. EVERYTHING ELSE — proprietary, all rights reserved
16
+ All other files in this repository, including without limitation the
17
+ server implementation (src/), tests, scripts and documentation, are the
18
+ confidential and proprietary property of Maffen Oy. No licence to use,
19
+ copy, modify or distribute them is granted by this file or by the
20
+ distribution of the MIT-licensed files above.
21
+
22
+
23
+ MIT License (applies to the files listed in section 1 only)
24
+
25
+ Copyright (c) 2026 Maffen Oy
26
+
27
+ Permission is hereby granted, free of charge, to any person obtaining a copy
28
+ of this software and associated documentation files (the "Software"), to deal
29
+ in the Software without restriction, including without limitation the rights
30
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
31
+ copies of the Software, and to permit persons to whom the Software is
32
+ furnished to do so, subject to the following conditions:
33
+
34
+ The above copyright notice and this permission notice shall be included in all
35
+ copies or substantial portions of the Software.
36
+
37
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
40
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
43
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,159 @@
1
+ # Salamailer
2
+
3
+ Zero-knowledge secure messaging for small regulated firms. Send a confidential
4
+ message + files to anyone (no account for the recipient) with true end-to-end
5
+ encryption — the server only ever holds ciphertext and keys it cannot unwrap.
6
+
7
+ **Installing from npm?** This package is the *client* — a zero-dependency CLI
8
+ and MCP server for the hosted service at [salamailer.fi](https://salamailer.fi).
9
+ Encryption happens on your machine; you need an account to send.
10
+
11
+ ```bash
12
+ npm install -g salamailer
13
+ salamailer init --origin https://salamailer.fi --email you@firm.fi
14
+ claude mcp add salamailer -- salamailer mcp # 12 tools for agents
15
+ ```
16
+
17
+ Jump to [CLI & MCP](#cli--mcp--the-ai-agent-channel) for the full tool surface
18
+ and the agent guardrails. The rest of this file documents the server, which is
19
+ proprietary and not part of the npm package (see `LICENSE`).
20
+
21
+ **Zero npm dependencies.** All integrations (Stripe, Twilio, Brevo/Postmark,
22
+ S3-compatible storage) are implemented with `fetch` + `node:crypto` — there is no
23
+ supply chain to audit beyond Node itself. See `DECISIONS.md` for how the build
24
+ maps to `PLAN.md`, and `DEPLOY.md` for production setup.
25
+
26
+ ## Run
27
+
28
+ Requires **Node ≥ 22.5** (built-in `node:sqlite`).
29
+
30
+ ```bash
31
+ npm start # → http://localhost:3000
32
+ npm test # 146-check e2e suite: crypto, replies, gates, billing, SSO, hardening, CLI/MCP
33
+ npm run hashes # SHA-256 of every served asset (publish for auditors)
34
+ ```
35
+
36
+ With no configuration, email/SMS print to the console + `data/outbox.log`,
37
+ billing is off (everyone's on trial), and blobs live on local disk. Copy
38
+ `.env.example` → fill in keys → each integration goes live independently.
39
+
40
+ ## The security model in one paragraph
41
+
42
+ Your browser generates a fresh AES-256-GCM content key per message and encrypts
43
+ everything (filenames included) before upload. The key travels **only in the URL
44
+ fragment** of the recipient's link — browsers never send fragments to servers. A
45
+ separate, rate-limited second channel (SMS code or passphrase) gates the
46
+ ciphertext download; it is *not* part of the key, so short codes can't be
47
+ brute-forced offline. The sender's copy of each key is wrapped under a vault key
48
+ wrapped under their password — so senders can re-send links, but a full server
49
+ breach yields nothing readable. Full threat model (including its honest limits):
50
+ `/security` in the running app.
51
+
52
+ ## Try the full flow
53
+
54
+ 1. **Register** (`/register`) — password is stretched locally; it never leaves the browser.
55
+ 2. **Compose** (`/compose`) — pick **Passphrase** for a fully-offline demo. Send.
56
+ 3. Open the link from the console/`data/outbox.log` in an incognito window,
57
+ enter the passphrase → decrypts in the browser.
58
+ 4. **Reply securely** — right on the read page, no account: the reply (text +
59
+ files) is encrypted in the recipient's browser under the sender's reply
60
+ public key, which travelled *inside* the encrypted message. Read it on `/app`.
61
+ 5. **Sent** (`/app`): resend link/code, revoke (real delete), export receipt,
62
+ inbox of encrypted replies.
63
+ 6. **Account** (`/account`): plan, quotas, Stripe upgrade (when configured);
64
+ team owners also get the admin console — per-member usage, an org
65
+ recipient-domain policy enforced server-side on every channel, and **SSO
66
+ (OIDC)** setup. SSO authenticates the session only; the vault still unlocks
67
+ with the user's password, client-side (`/unlock`) — IdPs never see keys.
68
+
69
+ Verify the zero-knowledge claim yourself: DevTools → Network during a send — all
70
+ request bodies are ciphertext and the `#key` fragment appears in no request. Then
71
+ inspect `data/salamailer.db` and `data/blobs/`.
72
+
73
+ ## CLI & MCP — the AI-agent channel
74
+
75
+ `cli/salamailer.mjs` is a zero-dependency CLI that reuses the *same* crypto module
76
+ the web app serves, so plaintext and keys exist only on the sending machine:
77
+
78
+ ```bash
79
+ salamailer init --origin https://app… --email you@firm.fi # password prompted, never stored
80
+ salamailer send --to client@x.fi --message "…" --file contract.pdf --burn [--self-relay] [--lang fi|en|sv]
81
+ salamailer list · replies · reply <id> [--save-dir d] · status · contacts · receipt <id> · resend <id> · resend-code <id> · revoke <id>
82
+ ```
83
+
84
+ `--self-relay` finalizes without handing the link to the server, so the content
85
+ key never reaches it even transiently — you deliver the printed link yourself
86
+ (parity with the web compose checkbox).
87
+
88
+ **Saved recipients** give a regular correspondent one *standing* gate passphrase
89
+ instead of a fresh one per message: `salamailer contacts add --to partner@co.fi
90
+ --passphrase "…"`, and every later send to them reuses it automatically (they
91
+ learn it once). The passphrase is stored encrypted under your vault key, synced
92
+ across web/CLI/MCP; the server never reads it. Manage the same list on `/account`.
93
+
94
+ `salamailer mcp` runs it as an **MCP server** (stdio), exposing twelve tools —
95
+ `send_secure_message` (with `self_relay` / `save_passphrase`),
96
+ `list_sent_messages`, `get_delivery_receipt`, `get_account_status`,
97
+ `resend_notification`, `resend_sms_code`, `revoke_message`, `save_contact`,
98
+ `list_contacts`, `delete_contact`, plus `list_replies` / `read_reply` (inbound
99
+ secure replies, decrypted locally) — full parity with the web dashboard's
100
+ sender surface, so any MCP-capable agent can run a complete round trip
101
+ (send → client replies → agent reads the reply) in zero-knowledge mail:
102
+
103
+ ```bash
104
+ claude mcp add salamailer -- salamailer mcp
105
+ ```
106
+
107
+ Transport auth is an **API key** (created on the `/account` page or by `init`;
108
+ stored SHA-256-hashed server-side; can't manage other keys). Agent sends are
109
+ metered against the same plan quotas — agents are senders, senders are who pays.
110
+ The credentials file (`~/.config/salamailer/config.json`, 0600) holds the API key
111
+ and the raw Vault Key: treat it like an SSH private key.
112
+
113
+ **Agent guardrails** (config file / `init` flags) make the MCP a policy
114
+ enforcement point for a prompt-injected agent:
115
+
116
+ - `"hideRecipients": true` (`--hide-recipients`) — privacy mode, strictest:
117
+ the model never sees recipient emails or phone numbers at all. Sends are
118
+ addressed by saved-contact **label** (`contact` arg; resolved locally inside
119
+ the CLI process), tool outputs mask every address, the standing passphrase is
120
+ not echoed, and contact management stays human-only. Implies contactsOnly.
121
+ For teams where even the recipient list is confidential to the AI vendor.
122
+ - `"contactsOnly": true` (`--contacts-only`) — the agent may only
123
+ send to recipients a **human** has saved as contacts; new recipients are
124
+ refused with instructions for out-of-band approval, and the agent channel
125
+ cannot add or remove contacts itself (no self-approval).
126
+ - `"allowedRecipientDomains": […]` (`--allow-domain`) — looser: restrict to
127
+ recipient domains.
128
+ - `"attachmentRoot": "/path"` — attachments may only come from inside this
129
+ directory (default: the cwd where `salamailer mcp` starts).
130
+
131
+ Every send is gated (SMS code / passphrase) and audit-logged server-side, so
132
+ the operator's answer to a mis-send attempt is: "the guardrails blocked it —
133
+ here is the log."
134
+
135
+ ## Layout
136
+
137
+ ```
138
+ src/
139
+ server.js routing, strict CSP, CSRF origin checks, static serving
140
+ handlers.js API endpoints (auth, messages, gate, billing, webhook)
141
+ config.js all env-driven configuration (see .env.example)
142
+ billing.js entitlements, Stripe REST + webhook signature verification
143
+ storage.js ciphertext blobs: S3 SigV4 adapter or local fs
144
+ notify.js email/SMS providers (console | brevo | postmark | twilio)
145
+ db.js node:sqlite schema (PLAN.md §7.2 + billing)
146
+ security.js scrypt hashing, tokens, HMAC (gates only — never key material)
147
+ ratelimit.js per-IP/user sliding windows
148
+ retention.js expiry sweep + real hard-delete
149
+ audit.js metadata-only audit log
150
+ public/
151
+ js/crypto.js the crypto heart — Web Crypto only, runs on the sender/recipient device
152
+ js/*.js page logic (register, login, compose, app, account, read)
153
+ cli/
154
+ salamailer.mjs CLI + MCP server (stdio JSON-RPC), reuses public/js/crypto.js
155
+ scripts/
156
+ hashes.mjs published build hashes ("verify us, not trust us")
157
+ test/
158
+ e2e.mjs boots the server, drives the real client crypto end-to-end
159
+ ```