arcane-os 0.18.0 → 0.19.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/CHANGELOG.md +15 -0
- package/README.md +1 -1
- package/docs/reference/cli.md +42 -16
- package/docs/reference/mail.md +65 -15
- package/docs/reviews/mail-server-purpose-review.md +48 -6
- package/package.json +1 -1
- package/src/cli/main.mjs +16 -17
- package/src/mail-credentials.mjs +101 -632
- package/src/mail.mjs +17 -26
- package/src/templates/workspace-template.mjs +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.19.0
|
|
4
|
+
|
|
5
|
+
- Read mail credentials from `.env.json` in the invocation directory through
|
|
6
|
+
portable Node file APIs. Remove the Windows Credential Manager subprocess,
|
|
7
|
+
embedded PowerShell/C# helper, helper timeouts, and transport-only machinery.
|
|
8
|
+
Existing Windows credential records remain untouched; populate the JSON file
|
|
9
|
+
explicitly when adopting this configuration change.
|
|
10
|
+
- Default mail commands to the top-level `RESEND_API_KEY`; `--profile mail`
|
|
11
|
+
selects that same key. Preserve named key set/status/delete operations through
|
|
12
|
+
exact `MAIL_PROFILES` entries, retaining unrelated JSON settings and naming
|
|
13
|
+
the missing setting and file before send or server startup.
|
|
14
|
+
- Ignore `.env.json` in the SDK checkout and newly scaffolded workspaces. Update
|
|
15
|
+
the mail reference and purpose-gate report with the portable configuration
|
|
16
|
+
contract and the reasons for each retained or removed operation.
|
|
17
|
+
|
|
3
18
|
## 0.18.0
|
|
4
19
|
|
|
5
20
|
- Adopt published `node-http-server` 10.0.0. PEM-backed development and
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ version-locked SDK runtime, while an integrated Arcane checkout uses its live
|
|
|
19
19
|
`arcane/` runtime. Both profiles preserve the same app URLs, theme, packaging,
|
|
20
20
|
event, cancellation, and browser run contracts.
|
|
21
21
|
|
|
22
|
-
This checkout defines the `0.
|
|
22
|
+
This checkout defines the `0.19.0` SDK contract. Applications pin one exact npm
|
|
23
23
|
version and lockfile; registry state is deliberately not baked into application
|
|
24
24
|
artifacts.
|
|
25
25
|
|
package/docs/reference/cli.md
CHANGED
|
@@ -32,7 +32,7 @@ and exits nonzero on failure. Machine output is defined by
|
|
|
32
32
|
| `arcane update-check` | Performs one explicit, read-only npm dist-tag query for the installed SDK version. |
|
|
33
33
|
| `arcane targets` | Lists target ids, declared status, formats, architectures, signing profiles, methods, and pairing reason. |
|
|
34
34
|
| `arcane repo status\|pull\|push` | Runs one selected repository operation for the current app workspace. |
|
|
35
|
-
| `arcane mail key set\|status\|delete` | Manages one server-only Resend API-key profile in
|
|
35
|
+
| `arcane mail key set\|status\|delete` | Manages one server-only Resend API-key profile in `.env.json`. |
|
|
36
36
|
| `arcane mail send` | Performs one explicit, idempotency-keyed Resend attempt from a complete JSON report on redirected stdin. |
|
|
37
37
|
| `arcane mail serve` | Starts one Arcane-to-Resend gateway with a server-only provider profile, a selected listener, and optional CORS and recipient configuration. |
|
|
38
38
|
|
|
@@ -813,22 +813,47 @@ npm exec -- arcane repo status
|
|
|
813
813
|
|
|
814
814
|
### Resend credential profiles
|
|
815
815
|
|
|
816
|
-
The
|
|
816
|
+
The mail commands read `.env.json` from the invocation directory on Windows,
|
|
817
|
+
Linux, and macOS. The credential subcommands select one profile in that file:
|
|
817
818
|
|
|
818
819
|
```text
|
|
819
|
-
arcane mail key set
|
|
820
|
-
arcane mail key status
|
|
821
|
-
arcane mail key delete
|
|
820
|
+
arcane mail key set [profile] [--secret-stdin]
|
|
821
|
+
arcane mail key status [profile]
|
|
822
|
+
arcane mail key delete [profile]
|
|
822
823
|
```
|
|
823
824
|
|
|
824
825
|
`key set` reads the Resend API key from a hidden terminal prompt. The
|
|
825
826
|
`--secret-stdin` form is for deliberately redirected non-interactive input and
|
|
826
|
-
rejects a TTY before reading. The key is
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
827
|
+
rejects a TTY before reading. The key is written to `.env.json` and is never
|
|
828
|
+
accepted in argv or returned in status output. The optional profile defaults
|
|
829
|
+
to `mail`, which selects top-level `RESEND_API_KEY`. Any other exact profile
|
|
830
|
+
selects `MAIL_PROFILES[profile].RESEND_API_KEY`, with no default-key fallback.
|
|
831
|
+
|
|
832
|
+
The minimal file is:
|
|
833
|
+
|
|
834
|
+
```json
|
|
835
|
+
{
|
|
836
|
+
"RESEND_API_KEY": ""
|
|
837
|
+
}
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
Fill in the key before starting mail, and add `.env.json` to the project's
|
|
841
|
+
`.gitignore`; the SDK repository already ignores it. Set and delete preserve
|
|
842
|
+
the file's other settings and profiles. Status returns the selected profile,
|
|
843
|
+
`provider:'resend'`, `storage:'.env.json'`, and `exists`. Delete returns
|
|
844
|
+
`exists:false` for both a removed and an already-absent credential.
|
|
845
|
+
|
|
846
|
+
Programmatic `createToolchain().mail(...)` resolves the configuration directory
|
|
847
|
+
from `cwd ?? workspaceRoot ?? process.cwd()`. This uses ordinary Node file
|
|
848
|
+
access rather than platform-specific credential processes. An Android host
|
|
849
|
+
supplies a compatible Node runtime and an accessible configuration directory.
|
|
850
|
+
Existing Windows Credential Manager records remain untouched; the JSON reader
|
|
851
|
+
does not migrate or fall back to them. Mail reads JSON directly and does not
|
|
852
|
+
populate or depend on process environment variables for this key.
|
|
853
|
+
|
|
854
|
+
Missing files or missing/empty selected keys stop `send` and `serve` with the
|
|
855
|
+
configuration path and exact JSON setting to fill in. Invalid JSON and file
|
|
856
|
+
access failures remain observable without printing credential content.
|
|
832
857
|
|
|
833
858
|
Machine output for `key set` requires `--secret-stdin`. Raw CLI arguments are
|
|
834
859
|
not included in acceptance events, and usage errors do not echo unknown option
|
|
@@ -840,7 +865,7 @@ or positional values.
|
|
|
840
865
|
server:
|
|
841
866
|
|
|
842
867
|
```text
|
|
843
|
-
arcane mail send --profile <profile> [--from <verified-sender>] --report-key <id> --report-stdin [--request-timeout <ms>]
|
|
868
|
+
arcane mail send [--profile <profile>] [--from <verified-sender>] --report-key <id> --report-stdin [--request-timeout <ms>]
|
|
844
869
|
```
|
|
845
870
|
|
|
846
871
|
`--report-stdin` is mandatory and rejects a terminal before attaching input
|
|
@@ -861,8 +886,8 @@ Resend owns their accepted shape. The adapter removes the application-only
|
|
|
861
886
|
`type` field and applies `--from` when supplied; otherwise the report or provider
|
|
862
887
|
template supplies the sender. Direct CLI sending has
|
|
863
888
|
no configured fallback recipients. The Resend credential comes only from the
|
|
864
|
-
selected
|
|
865
|
-
accepted through argv or environment variables.
|
|
889
|
+
selected `.env.json` profile; omitting `--profile` selects `mail`. Neither the
|
|
890
|
+
key nor report content is accepted through argv or process environment variables.
|
|
866
891
|
|
|
867
892
|
The caller owns the nonempty `--report-key`, which is forwarded unchanged.
|
|
868
893
|
Reuse the same key only with the same
|
|
@@ -883,10 +908,11 @@ loss after the attempt begins is ambiguous because Resend may have accepted it.
|
|
|
883
908
|
`mail serve` starts one owned Node HTTP gateway:
|
|
884
909
|
|
|
885
910
|
```text
|
|
886
|
-
arcane mail serve --profile <profile> [--from <verified-sender>] [--app <label>] [--origin <exact-origin>] [--allow-to <addresses>] [--host 0.0.0.0] [--port 8025] [--request-timeout <ms>]
|
|
911
|
+
arcane mail serve [--profile <profile>] [--from <verified-sender>] [--app <label>] [--origin <exact-origin>] [--allow-to <addresses>] [--host 0.0.0.0] [--port 8025] [--request-timeout <ms>]
|
|
887
912
|
```
|
|
888
913
|
|
|
889
|
-
The selected
|
|
914
|
+
The selected `.env.json` profile supplies only the server-side Resend API key;
|
|
915
|
+
omitting `--profile` selects `mail`.
|
|
890
916
|
The CLI does not read a browser app key. Its optional `--app` value labels the
|
|
891
917
|
server; the incoming request's `X-Mail-App` identifies the application for
|
|
892
918
|
subscription verification. The HTTP authentication contract pairs that
|
package/docs/reference/mail.md
CHANGED
|
@@ -13,12 +13,14 @@ gateway and is never included in browser or WebAssembly state.
|
|
|
13
13
|
| `MailTransport.mjs` | Browser, WebView, or compatible Fetch host | Sends one already-persisted request to the configured Arcane gateway with the stable report key as its idempotency key. |
|
|
14
14
|
| `arcane mail send` | Node on the local machine | Reads one complete provider-neutral report from redirected stdin and performs one explicit Resend attempt with a caller-owned idempotency key. |
|
|
15
15
|
| `arcane mail serve` | Node on the configured host | Owns caller verification, protects the provider credential, applies explicitly configured recipient and origin settings, and makes one server-side Resend request. |
|
|
16
|
-
| `arcane mail key ...` | Node on Windows | Stores, inspects, or deletes a Resend API key in
|
|
16
|
+
| `arcane mail key ...` | Node on Windows, Linux, or macOS | Stores, inspects, or deletes a Resend API key in the selected `.env.json`. |
|
|
17
17
|
|
|
18
18
|
The browser never receives the Resend API key. The gateway never writes that
|
|
19
19
|
key to source, argv, logs, events, fixtures, browser storage, or its public
|
|
20
|
-
lifecycle result.
|
|
21
|
-
|
|
20
|
+
lifecycle result. The CLI and gateway use the same Node filesystem and network
|
|
21
|
+
interfaces on Windows, Linux, and macOS. An Android host supplies a compatible
|
|
22
|
+
Node runtime and an accessible configuration directory; the mail implementation
|
|
23
|
+
contains no Windows credential process or platform-specific path convention.
|
|
22
24
|
|
|
23
25
|
## Public npm import
|
|
24
26
|
|
|
@@ -42,8 +44,8 @@ import Mail,{
|
|
|
42
44
|
and `sendMailReport`.
|
|
43
45
|
|
|
44
46
|
This entrypoint contains only the portable browser/WebView runtime, outbox, and
|
|
45
|
-
transport contract. It does not import the Node HTTP gateway or
|
|
46
|
-
|
|
47
|
+
transport contract. It does not import the Node HTTP gateway or its filesystem
|
|
48
|
+
credential adapter. Programmatic developer tooling reaches those
|
|
47
49
|
host-owned operations through the existing `createToolchain().mail(...)`
|
|
48
50
|
boundary; ordinary operators use `arcane mail send`, `arcane mail serve`, and
|
|
49
51
|
`arcane mail key ...`. This keeps Node credential and server authority out of a
|
|
@@ -53,9 +55,9 @@ browser import while preserving one shared CLI/toolchain implementation.
|
|
|
53
55
|
|
|
54
56
|
Arcane Mail deliberately separates two credentials:
|
|
55
57
|
|
|
56
|
-
- The **Resend API key** is provider authority.
|
|
57
|
-
|
|
58
|
-
`mail
|
|
58
|
+
- The **Resend API key** is provider authority. The Node process reads it from
|
|
59
|
+
`.env.json`. `arcane mail key set [profile]` can store it there through hidden
|
|
60
|
+
input; `mail send` and `mail serve` read the selected profile inside that process.
|
|
59
61
|
- The **subscription key** is the application user's subscription credential.
|
|
60
62
|
When present, the browser sends it as `Authorization: Bearer <subscriptionKey>`,
|
|
61
63
|
with the exact application name in `X-Mail-App`. The application name identifies the
|
|
@@ -270,21 +272,69 @@ committed acceptance result.
|
|
|
270
272
|
|
|
271
273
|
## Operate the CLI and gateway
|
|
272
274
|
|
|
273
|
-
|
|
275
|
+
Create `.env.json` in the directory from which the mail command runs, then fill
|
|
276
|
+
in the provider key:
|
|
277
|
+
|
|
278
|
+
```json
|
|
279
|
+
{
|
|
280
|
+
"RESEND_API_KEY": ""
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
The SDK repository ignores `.env.json`. Keep the same entry in a consuming
|
|
285
|
+
project's `.gitignore`. This is a JSON configuration file; the mail commands
|
|
286
|
+
read it directly without copying its contents into `process.env`.
|
|
287
|
+
|
|
288
|
+
The default profile is `mail`, which selects top-level `RESEND_API_KEY`.
|
|
289
|
+
The explicit `--profile mail` form selects the same setting. Other profile names
|
|
290
|
+
select exact entries under `MAIL_PROFILES`:
|
|
291
|
+
|
|
292
|
+
```json
|
|
293
|
+
{
|
|
294
|
+
"RESEND_API_KEY": "",
|
|
295
|
+
"MAIL_PROFILES": {
|
|
296
|
+
"another-provider-account": {
|
|
297
|
+
"RESEND_API_KEY": ""
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
An absent named profile does not fall back to the default key. The profile
|
|
304
|
+
selects Resend provider credentials; it is separate from the incoming
|
|
305
|
+
application name and subscriber key.
|
|
306
|
+
|
|
307
|
+
Programmatic operations resolve `.env.json` from `options.cwd`, then
|
|
308
|
+
`options.workspaceRoot`, then `process.cwd()`, choosing the first supplied
|
|
309
|
+
directory. The CLI uses its invocation directory. There is no upward directory
|
|
310
|
+
search or dependency on a Windows installation directory or temporary-directory
|
|
311
|
+
environment variable.
|
|
312
|
+
|
|
313
|
+
The existing key commands manage the same file:
|
|
274
314
|
|
|
275
315
|
```text
|
|
276
|
-
arcane mail key set
|
|
277
|
-
arcane mail key status
|
|
278
|
-
arcane mail key delete
|
|
316
|
+
arcane mail key set
|
|
317
|
+
arcane mail key status
|
|
318
|
+
arcane mail key delete
|
|
279
319
|
```
|
|
280
320
|
|
|
281
321
|
`key set` prompts with hidden input. `--secret-stdin` is the explicit
|
|
282
|
-
non-interactive alternative and rejects a TTY.
|
|
322
|
+
non-interactive alternative and rejects a TTY. Each command accepts an optional
|
|
323
|
+
profile argument, defaulting to `mail`. Set and delete preserve other JSON
|
|
324
|
+
settings and profiles; status reports existence without returning the key.
|
|
325
|
+
Results identify `storage: '.env.json'`. An already-absent deletion succeeds
|
|
326
|
+
with `exists: false`.
|
|
327
|
+
|
|
328
|
+
Existing Windows Credential Manager records remain untouched. The JSON path
|
|
329
|
+
does not read, migrate, or delete those records; populate the selected JSON
|
|
330
|
+
setting to use it. Missing files or missing/empty provider settings produce an
|
|
331
|
+
actionable startup/send error naming the file and exact setting. Unreadable or
|
|
332
|
+
invalid JSON is reported without including credential content in the error.
|
|
283
333
|
|
|
284
334
|
Perform one provider attempt directly from the SDK CLI:
|
|
285
335
|
|
|
286
336
|
```text
|
|
287
|
-
arcane mail send --
|
|
337
|
+
arcane mail send --from "Arcane <verified@example.com>" --report-key <stable-id> --report-stdin
|
|
288
338
|
```
|
|
289
339
|
|
|
290
340
|
The redirected UTF-8 JSON object is read completely. Its fields and values
|
|
@@ -312,7 +362,7 @@ deadline.
|
|
|
312
362
|
Start the gateway:
|
|
313
363
|
|
|
314
364
|
```text
|
|
315
|
-
arcane mail serve --profile
|
|
365
|
+
npm exec -- arcane mail serve --profile mail --host 0.0.0.0 --port 8025
|
|
316
366
|
```
|
|
317
367
|
|
|
318
368
|
The default listener is `0.0.0.0:8025`; `--host` and `--port` select its bind
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
[Roshi's Codex PRIME] The
|
|
1
|
+
[Roshi's Codex PRIME] The gateway review and its portable credential follow-up apply the same three purpose gates. The gateway removals preserve complete delivery outcomes, configured CORS and recipient policy, and the optional subscription verifier. Mail credentials now belong in the user-selected `.env.json`, with one portable Node implementation for Windows, Linux, and macOS and a compatible Node host as the Android adaptation boundary. The original gateway inventory below uses baseline `f055e05`; the credential follow-up reviews the Windows storage implementation present at SDK `0.18.0`. Commit, package, publication, and actual platform execution outcomes belong to the delivery record rather than to this source-review report.
|
|
2
2
|
|
|
3
3
|
| Decision | Behavior | Why it matters | Source status at this review |
|
|
4
4
|
| --- | --- | --- | --- |
|
|
5
5
|
| Remove | Loopback-only admission, manual header reconstruction, address/origin normalization, local payload grammar, manual content-length scan, dead queue knobs | These restrict supported use, rewrite supplied values, or repeat work already owned elsewhere. | Removed from the inspected source; naming-only changes carry no runtime claim. |
|
|
6
6
|
| Remove and replace | Local app-key hashes and X-Mail-Key authentication | The selected application plus bearer subscription contract needs an actual verifier, not a local shared-key comparison. | Removed. Optional `verifySubscription` implements the local integration contract; the remote endpoint adapter remains future work. Initial setup without that callback performs no caller authentication. |
|
|
7
|
+
| Remove and replace | Windows-only credential process, native interop, helper timers, environment prerequisites, encoding and input/output limits | They prevent the required portable startup and perform work absent from the selected JSON storage path. | Replaced by direct `.env.json` access. Existing Windows credential records remain untouched. Default and named credential operations remain useful and are preserved. |
|
|
7
8
|
| Simplify | Exact CORS policy, one configuration resolution, one provider request construction/serialization, optional observation | Preserve the intended result while eliminating repeated parsing, copies, policy structures, and absent-observer work. | Present in inspected source. Explicit nonempty origin lists remain authoritative; otherwise the nominal-domain default applies. |
|
|
8
9
|
| Keep | Configured subscription authentication, explicit recipient policy/fallback, stable idempotency, real cancellation/deadlines, complete provider outcomes, concurrent requests and owned shutdown | These have concrete callers and determine whether mail is sent, retried, reported honestly, or stopped correctly. | Retained. Only a verifier result of `true` permits authenticated delivery; failures and cancellation prevent the provider attempt. |
|
|
9
10
|
|
|
10
|
-
This review covers every named method, callback owner, constant, and significant action in `src/mail-server.mjs` at baseline `f055e05`, plus the coupled CLI/browser paths needed to preserve behavior. The current-source observations were reconciled on 2026-09-07.
|
|
11
|
+
This review covers every named method, callback owner, constant, and significant action in `src/mail-server.mjs` at baseline `f055e05`, plus the coupled CLI/browser paths needed to preserve behavior. The portable credential addendum covers `src/mail-credentials.mjs`, its command/CLI callers, and the ignored configuration file. The current-source observations were reconciled on 2026-09-07. Source simplification is distinct from measured speedup, successful live mail delivery, release evidence, and execution on a particular platform.
|
|
11
12
|
|
|
12
13
|
The intended software outcome is one complete mail request reaching the configured provider through the owning gateway, followed by an honest complete result. The gateway must preserve caller content, useful recipient policy, configured subscription authentication, explicit allowed-origin CORS behavior, caller cancellation, provider acceptance and uncertainty, and owned shutdown. It must use the published `node-http-server` interface and work on the selected host/domain without a loopback-only assumption. The initial unauthenticated setup is an implementation assumption stated to the user; no live deployment is selected in this work.
|
|
13
14
|
|
|
@@ -19,7 +20,7 @@ The user's three gates apply to the behavior, not to how impressive a helper nam
|
|
|
19
20
|
|
|
20
21
|
In the tables, `Y/Y/N` means keep the behavior; `Y/Y/Y` means simplify it by removing the identified redundant step; `N/N/Y` means remove the behavior. `Pending` identifies a real unresolved external contract or compatibility question, not an automatic delivery hold. Cost descriptions identify allocations, scans, calls, listeners, or waits that source removal eliminates; they are not timing measurements. A rename alone has no claimed runtime benefit. The tables state the current source name when behavior remains; recommendations and pending changes are marked explicitly. Existing public entry-point names remain stable unless the user expressly changes their contract.
|
|
21
22
|
|
|
22
|
-
The review sources are [the gateway](../../src/mail-server.mjs), [mail command orchestration](../../src/mail.mjs), [CLI server ownership](../../src/cli/main.mjs), [the portable transport](../../runtime/arcane/modules/MailTransport.mjs), [the browser Mail owner](../../runtime/arcane/modules/Mail.js), [the outbox's result consumers](../../runtime/arcane/modules/MailOutbox.mjs), and [the public mail reference](../reference/mail.md). Outbox storage, scheduling, maintenance methods, and unrelated SDK modules are outside this cleanup scope. Relevant test source was read as evidence of expected interfaces; tests were not executed.
|
|
23
|
+
The review sources are [the gateway](../../src/mail-server.mjs), [mail credentials](../../src/mail-credentials.mjs), [mail command orchestration](../../src/mail.mjs), [CLI server ownership](../../src/cli/main.mjs), [the portable transport](../../runtime/arcane/modules/MailTransport.mjs), [the browser Mail owner](../../runtime/arcane/modules/Mail.js), [the outbox's result consumers](../../runtime/arcane/modules/MailOutbox.mjs), and [the public mail reference](../reference/mail.md). Outbox storage, scheduling, maintenance methods, and unrelated SDK modules are outside this cleanup scope. Relevant test source was read as evidence of expected interfaces; tests were not executed.
|
|
23
24
|
|
|
24
25
|
Resend's send API accepts recipient strings or arrays, additional provider fields, and templates whose content rules differ from ordinary text/HTML messages. Its response includes a provider identifier. These are reasons to retain provider-boundary mapping while removing narrower gateway schemas. [Resend Send Email](https://resend.com/docs/api-reference/emails/send-email)
|
|
25
26
|
|
|
@@ -34,6 +35,7 @@ The latest user-selected boundaries govern this review:
|
|
|
34
35
|
- Explicit recipient allowlists and error-recipient fallback remain. A configured nonempty allowlist covers resolved `to`, `cc`, and `bcc` recipients; allowing additional provider fields must not bypass the selected recipient policy. With no allowlist, the policy performs no recipient scan. Per-address trimming, lowercasing, and local email grammar do not remain.
|
|
35
36
|
- Caller-selected deadlines, the cancellation adapter, complete provider outcomes, retry classification, stable idempotency, and independently constructed snapshots for emitted observations remain. Shared mutable provider snapshots have not been selected.
|
|
36
37
|
- Remove manual response content-length scanning, duplicate configuration/CORS construction, unobserved event payload creation, unused `allowZero`, and unused CLI queue options.
|
|
38
|
+
- All SDK mail operations require a functional Windows, Linux, and macOS path, with Android adaptation at the host boundary. Read provider configuration directly from an ignored `.env.json`; an operating-system limitation notice does not satisfy the portable requirement. Default profile `mail` uses `RESEND_API_KEY`; other exact names use `MAIL_PROFILES[profile].RESEND_API_KEY`. Missing configuration must identify the file and setting to fill in. Existing Windows records remain untouched, without an automatic read or migration.
|
|
37
39
|
|
|
38
40
|
Source inspection during this report's handoff shows local app/email/sender/API-key format validators and loopback admission removed; origin values retained in one Set; complete provider JSON values or raw response text preserved; template-shaped provider requests no longer required to supply local text/HTML fields; and configuration passed once into `createConfiguredMailHandler`. Observer objects, pending Sets, callback closures, event payload reconstruction, and drain waits are absent when `onEvent` is omitted. An already-cancelled direct send stops before provider payload construction or serialization. Response writing no longer calculates content length. The fixed `/v1/mail` route accepts its query-bearing form, matching the portable endpoint parser. These are source observations, not executed behavior.
|
|
39
41
|
|
|
@@ -309,13 +311,13 @@ The coupled CLI/browser actions below are necessary to avoid leaving contradicto
|
|
|
309
311
|
|
|
310
312
|
| Coupled path/action | Gates and decision | Required result and evidence boundary |
|
|
311
313
|
| --- | --- | --- |
|
|
312
|
-
| `src/mail.mjs` `serveMail` old hidden app-key input and local auth options | Y/Y/Y — Reconciled with the optional verifier contract. | Obsolete shared-app-key prompt/options are removed. Current `serveMailGateway` forwards `options.verifySubscription` to the server and keeps provider credential
|
|
314
|
+
| `src/mail.mjs` `serveMail` old hidden app-key input and local auth options | Y/Y/Y — Reconciled with the optional verifier contract. | Obsolete shared-app-key prompt/options are removed. Current `serveMailGateway` forwards `options.verifySubscription` to the server and keeps provider credential operations and the provider API key separate. The portable follow-up resolves that provider credential from `.env.json`. |
|
|
313
315
|
| `src/mail.mjs` `recipientList` and origin list preparation | Y/Y/Y — Remove redundant rewriting, preserve configured policy values. | Gateway and CLI must not disagree about preserved recipients/origins. An exact allowlist is still an intentional policy. |
|
|
314
316
|
| `src/mail.mjs` `withoutMailCredentials` | N/N/Y for recursive payload-key redaction — Remove. | It recursively traversed/copied complete results and deleted any content property named `apiKey` or `appKey`, including ordinary user payload fields. Keep actual transport credentials outside constructed public results at their owning boundary; do not rewrite the report based on property names. Source inspection shows this traversal removed. |
|
|
315
317
|
| `src/mail.mjs` `completeSendFailure` | Y/Y/Y — Remove helper after preserving the complete result directly. | Failure output still contains the provider result, or a narrow wrapper for a non-object injected result. No recursive copying/redaction pass remains. |
|
|
316
318
|
| CLI queue fields passed to a server that no longer consumes them | N/N/Y — Remove dead options/help/forwarding together. | `bodyQueueTimeoutMs`, `maxConcurrentBodyReads`, `maxConcurrentSends`, `maxQueuedBodyReads`, `maxQueuedSends`, and `sendQueueTimeoutMs` have no consumer in baseline mail-server configuration. Do not preserve misleading knobs or recreate queues for them. |
|
|
317
319
|
| CLI app-slug validation/help and server-wide app identity requirement | Y/Y/Y — Remove app restriction and shared-server assumption. | Application is an ordinary per-request string; any app may use the server. Do not locally lowercase, rewrite, enumerate, or compare it to one server app. |
|
|
318
|
-
| CLI mandatory `--from` | Y/Y/Y — Remove the redundant requirement; retain an explicit sender override. | The report or provider template may supply its sender. Requiring a shared CLI sender blocked those supported requests and per-report sender selection. Both mail commands
|
|
320
|
+
| CLI mandatory `--from` | Y/Y/Y — Remove the redundant requirement; retain an explicit sender override. | The report or provider template may supply its sender. Requiring a shared CLI sender blocked those supported requests and per-report sender selection. Both mail commands accept optional `--from`; Resend owns sender validity. The stable send key remains required. The portable credential follow-up makes the profile optional, defaulting to `mail`. |
|
|
319
321
|
| `MailTransport.mjs` request-header construction | Y/Y/Y — Updated to optional bearer Authorization; retain X-Mail-App. | Current source accepts an omitted, null, or empty subscription key and omits Authorization when no key exists, allowing the initial server setup. A supplied nonempty string is sent unchanged as the bearer credential; a configured server owns rejection of missing/invalid authentication. Preserve complete report payload, exact report key, cancellation, and configured destination. Never use the subscriber key as Resend provider authority. |
|
|
320
322
|
| `MailTransport.mjs` provider ID/error code regexes | Y/Y/Y — Simplified in current source. | Provider ID requires an actual nonempty string, and provider error codes remain complete strings. The gateway and browser no longer disagree about provider metadata syntax. |
|
|
321
323
|
| Browser/native/outbox request-ID grammar | Y/Y/N — Preserve; broader relaxation deferred. | Request-ID consumers still require the existing form. No half-implemented grammar change is included in this cleanup. |
|
|
@@ -344,8 +346,48 @@ The final status and intentionally deferred work are explicit:
|
|
|
344
346
|
| Native-only request/response interface narrowing | Deferred and preserved. The non-Buffer request-chunk conversion, reader-based provider body interface, cancellation adapter, and invalid injected-status classification remain. Removing them requires proving that supported injected callers do not need them. | No claim of safe removal or measured saving is made for these retained compatibility paths. |
|
|
345
347
|
| Escaped handler and event observer failure reporting | Implemented. Both call `reportMailError`, which uses native `inspect` with depth/array/string clipping disabled before stderr output. `closeResponseAfterHandlerFailure(error)` also passes the error to response destruction; observer errors do not change delivery classification. | Source evidence only; formatting work occurs only on failure. No local error-injection or runtime check was performed for this report. |
|
|
346
348
|
| Remaining local lifecycle rejection-observer names | Preserved. Their narrow purpose is preventing duplicate unhandled rejections while the public lifecycle/close promise retains failure. The inventory records their actual names; no additional abstraction or blanket naming rewrite is selected. | Do not report every possible naming improvement as implemented. |
|
|
347
|
-
| Final source delivery evidence |
|
|
349
|
+
| Final source delivery evidence | The original gateway review selected `0.16.0`; this portable credential follow-up extends the source present in `0.18.0`. Root owns final source/documentation reconciliation and the actual next commit/package/publication outcome. | This report supplies source-review evidence. Actual platform execution, live send, and remote subscription verification remain separate evidence boundaries. The pending remote adapter does not hold the current increment. |
|
|
348
350
|
|
|
349
351
|
TWiNStripeServer owns the future `verify-subscription` endpoint adapter contract; the SDK owns the mail request's application/token consumption and the provider-attempt decision. Any application can use the shared service under its selected CORS policy. The current optional callback allows delivery now without pretending the remote adapter exists: omitted means initial setup with no caller authentication, configured means only an affirmative verifier result permits provider delivery. The later endpoint integration remains recorded and does not cancel or block this current increment.
|
|
350
352
|
|
|
351
353
|
No local test, lint, type check, validation build, browser QA, performance measurement, or live Resend send was performed for this report. No Git index, commit, branch, push, package, or publication operation was performed by the report author. Every baseline named method and callback owner, constant, and significant action remains represented above; removed names are retained in the inventory for traceability. Root owns source/documentation delivery and selected-output verification under its governing authorization. This report records source inspection, not release or runtime evidence.
|
|
354
|
+
|
|
355
|
+
## Portable credential follow-up: 0.19.0
|
|
356
|
+
|
|
357
|
+
The selected storage owner is `src/mail-credentials.mjs`. The public CLI and
|
|
358
|
+
`createToolchain().mail(...)` reach it through `src/mail.mjs`. Repository caller
|
|
359
|
+
tracing found the old Windows target helpers only in this owner and its direct
|
|
360
|
+
test source; the package has no `./mail-credentials` public subpath. The supported
|
|
361
|
+
key operations remain, with JSON replacing their Windows storage implementation.
|
|
362
|
+
Existing native credential records are neither read nor changed.
|
|
363
|
+
|
|
364
|
+
| Method or action | Gates | Decision, callers, and concrete purpose |
|
|
365
|
+
| --- | --- | --- |
|
|
366
|
+
| `WINDOWS_CREDENTIAL_HELPER`, its C# `Write`, `Read`, `Exists`, `Delete`, native structs, and Advapi32 imports | N/N/Y | Remove. These existed solely to reach Windows Credential Manager; all four operations now use the selected JSON owner. No child process or native compilation remains. |
|
|
367
|
+
| `WINDOWS_CREDENTIAL_HELPER_COMMAND`, `powershellExecutable`, `helperArguments`, `helperEnvironment`, `validatePlatform`, `helperOptions` | N/N/Y | Remove. Windows path discovery, environment reconstruction, command encoding, and injected Windows runner options have no consumer in portable JSON storage. `SystemRoot`, `WINDIR`, `TEMP`, and `TMP` are no longer mail credential prerequisites. |
|
|
368
|
+
| `runCredentialProcess`, `runWindowsCredentialHelper`, `invokeCredentialHelper` and their nested process callbacks | N/N/Y | Remove. File operations replace spawning, helper stdin/stdout accumulation, process listeners, timeout allocation, and process shutdown. |
|
|
369
|
+
| `serializeRequest`, `parseResponse`, `decodeSecret`, `BASE64_PATTERN`, helper input/output and credential limits | N/N/Y | Remove. There is no helper transport to encode, decode, scan, or limit. The complete configuration is parsed as JSON at its actual owner. |
|
|
370
|
+
| `validateMailCredentialProfile`, `mailCredentialTarget`, `RESEND_CREDENTIAL_TARGET_PREFIX`, `PROFILE_PATTERN`, `MAX_PROFILE_LENGTH` | N/N/Y for the native target machinery | Remove. JSON profile keys need no native target namespace or lowercase/length grammar. Existing names remain usable; arbitrary nonempty string names are selected exactly. |
|
|
371
|
+
| `validateSecret`, `SECRET_PATTERN`, `validateTimeout`, helper timeout constants | Y/Y/Y for reading a usable key; N/N/Y for helper restrictions | Remove the helper-specific grammar and deadline machinery. A key must be a nonempty string to supply provider authorization; Resend owns its acceptance. No helper deadline is needed. |
|
|
372
|
+
| `usageError`, `unavailableError`, `operationError`, `cancellationError`, `assertNotAborted`, `validateOptions` | Y/Y/Y | Remove the Windows error wrappers and duplicate option/cancellation scaffolding. File failures surface at the file owner; shared `throwIfAborted` handles cancellation. Errors identify the file or setting without including secret values. |
|
|
373
|
+
| `mailCredentialLocation` | Y/Y/N | Keep. All four operations and the missing-key reporter need the same directory/profile mapping. It selects `cwd`, then `workspaceRoot`, then `process.cwd()`, with no directory search. Default `mail` maps to top-level `RESEND_API_KEY`; another exact name maps to its `MAIL_PROFILES` entry. |
|
|
374
|
+
| `readMailSettings` | Y/Y/N | Keep. Each credential operation reads the complete selected file once. Missing files support read/status/delete absence; unreadable JSON cannot supply settings. Parser excerpts are omitted because they may contain the credential. |
|
|
375
|
+
| `mailProfileSettings` | Y/Y/N | Keep. Read, status, set, and delete need the same exact default/named selection. An absent named profile stays absent, including after deletion; it never reveals the default account's key. Object checks identify configuration that cannot represent the requested profile. |
|
|
376
|
+
| `configuredMailKey` | Y/Y/N | Keep. Read and status share missing/empty-key semantics and a useful error for a value that cannot be a provider key. The returned key is preserved exactly. |
|
|
377
|
+
| `mailCredentialStatus` | Y/Y/N | Keep the shared status shape for set/status/delete: profile, provider, storage, and existence. This preserves the useful operation result without returning the credential. |
|
|
378
|
+
| `setMailCredential`, `readMailCredential`, `getMailCredentialStatus`, `deleteMailCredential` | Y/Y/Y | Preserve all four operations and replace their implementation. Explicit set/delete update only the selected credential field while preserving other settings and profiles; read returns the selected key or null; status reports existence. File access uses portable Node APIs. |
|
|
379
|
+
| `mailCredentialOptions`, `readMailProviderKey` | Y/Y/Y | Keep shared command configuration and consolidate the real send/serve duplicate key-read error path. Remove Windows runner, helper directory, and helper timeout forwarding. Missing configuration names the exact JSON setting and file before opening a listener or attempting delivery. |
|
|
380
|
+
| CLI required profile flag and omitted invocation directory | Y/Y/Y | Make the profile optional with default `mail`, retaining explicit profiles. Forward the invocation directory through every mail operation, so the CLI reads the operator's selected file. Preserve hidden/stdin key entry and existing send/serve lifecycle. |
|
|
381
|
+
| Repository and workspace-template `.gitignore` entry | Y/Y/N | Keep `.env.json` out of source control in this checkout and newly generated workspaces. The populated local file is outside the committed and published change. |
|
|
382
|
+
|
|
383
|
+
The credential path performs one file read per selected operation and one write
|
|
384
|
+
only for an explicit set or a deletion that finds a credential. Startup reads
|
|
385
|
+
once; there is no polling, watcher, background cache, migration, or platform
|
|
386
|
+
process. These are source-level work reductions, not a measured speedup.
|
|
387
|
+
|
|
388
|
+
The same Node filesystem contract covers Windows, Linux, and macOS. Android
|
|
389
|
+
requires a compatible Node host and accessible configuration directory. This
|
|
390
|
+
review does not claim execution on those platforms or an actual provider send.
|
|
391
|
+
The updated test source covers disposable JSON storage, exact named selection,
|
|
392
|
+
preservation of other settings, missing settings, credential-free error output,
|
|
393
|
+
cancellation before writes, and CLI defaults. Local tests and checks were not run.
|
package/package.json
CHANGED
package/src/cli/main.mjs
CHANGED
|
@@ -81,11 +81,11 @@ Usage:
|
|
|
81
81
|
${CLI_NAME} update-check
|
|
82
82
|
${CLI_NAME} targets
|
|
83
83
|
${CLI_NAME} repo status|pull|push
|
|
84
|
-
${CLI_NAME} mail key set
|
|
85
|
-
${CLI_NAME} mail key status
|
|
86
|
-
${CLI_NAME} mail key delete
|
|
87
|
-
${CLI_NAME} mail send --profile <profile> [--from <address>] --report-key <id> --report-stdin [--request-timeout <ms>]
|
|
88
|
-
${CLI_NAME} mail serve --profile <profile> [--from <address>] [--app <label>] [--origin <origin>] [--allow-to <addresses>] [--host 0.0.0.0] [--port 8025] [--request-timeout <ms>]
|
|
84
|
+
${CLI_NAME} mail key set [profile] [--secret-stdin]
|
|
85
|
+
${CLI_NAME} mail key status [profile]
|
|
86
|
+
${CLI_NAME} mail key delete [profile]
|
|
87
|
+
${CLI_NAME} mail send [--profile <profile>] [--from <address>] --report-key <id> --report-stdin [--request-timeout <ms>]
|
|
88
|
+
${CLI_NAME} mail serve [--profile <profile>] [--from <address>] [--app <label>] [--origin <origin>] [--allow-to <addresses>] [--host 0.0.0.0] [--port 8025] [--request-timeout <ms>]
|
|
89
89
|
|
|
90
90
|
Development:
|
|
91
91
|
--public Bind dev to all IPv4 interfaces (0.0.0.0) and print network URLs.
|
|
@@ -657,9 +657,9 @@ function operationOptions(command,parsed,cwd){
|
|
|
657
657
|
if(area==='key'){
|
|
658
658
|
noExtraPositionals(command,positionals,3);
|
|
659
659
|
const action=positionals[1];
|
|
660
|
-
const profile=positionals[2];
|
|
661
|
-
if(!['set','status','delete'].includes(action)
|
|
662
|
-
usage('mail key requires set, status, or delete
|
|
660
|
+
const profile=positionals[2]??'mail';
|
|
661
|
+
if(!['set','status','delete'].includes(action)){
|
|
662
|
+
usage('mail key requires set, status, or delete, with an optional profile id.');
|
|
663
663
|
}
|
|
664
664
|
if(values.profile!==undefined||values.from!==undefined||values.app!==undefined
|
|
665
665
|
||values.origin!==undefined
|
|
@@ -675,6 +675,7 @@ function operationOptions(command,parsed,cwd){
|
|
|
675
675
|
}
|
|
676
676
|
return {
|
|
677
677
|
action:`key-${action}`,
|
|
678
|
+
cwd,
|
|
678
679
|
profile,
|
|
679
680
|
secretStdin:flags.has('secret-stdin'),
|
|
680
681
|
};
|
|
@@ -687,10 +688,10 @@ function operationOptions(command,parsed,cwd){
|
|
|
687
688
|
if(flags.has('report-stdin')||values['report-key']!==undefined){
|
|
688
689
|
usage('--report-stdin and --report-key are supported only by mail send.');
|
|
689
690
|
}
|
|
690
|
-
if(!values.profile)usage('mail serve requires --profile <value>.');
|
|
691
691
|
return {
|
|
692
692
|
action:'serve',
|
|
693
|
-
|
|
693
|
+
cwd,
|
|
694
|
+
profile:values.profile??'mail',
|
|
694
695
|
from:values.from,
|
|
695
696
|
appId:values.app,
|
|
696
697
|
origin:values.origin,
|
|
@@ -710,25 +711,23 @@ function operationOptions(command,parsed,cwd){
|
|
|
710
711
|
||values.port!==undefined){
|
|
711
712
|
usage('mail send does not accept gateway server options.');
|
|
712
713
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
'report-key':values['report-key'],
|
|
716
|
-
})){
|
|
717
|
-
if(!value)usage(`mail send requires --${name} <value>.`);
|
|
714
|
+
if(!values['report-key']){
|
|
715
|
+
usage('mail send requires --report-key <value>.');
|
|
718
716
|
}
|
|
719
717
|
if(!flags.has('report-stdin')){
|
|
720
718
|
usage('mail send requires --report-stdin.');
|
|
721
719
|
}
|
|
722
720
|
return {
|
|
723
721
|
action:'send',
|
|
724
|
-
|
|
722
|
+
cwd,
|
|
723
|
+
profile:values.profile??'mail',
|
|
725
724
|
from:values.from,
|
|
726
725
|
reportKey:values['report-key'],
|
|
727
726
|
reportStdin:true,
|
|
728
727
|
requestTimeout:readMailRequestTimeout(values['request-timeout']),
|
|
729
728
|
};
|
|
730
729
|
}
|
|
731
|
-
usage('mail requires key set|status|delete
|
|
730
|
+
usage('mail requires key set|status|delete [profile], send, or serve.');
|
|
732
731
|
}
|
|
733
732
|
usage(`Unknown command. Run ${CLI_NAME} --help for usage.`);
|
|
734
733
|
}
|