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 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.18.0` SDK contract. Applications pin one exact npm
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
 
@@ -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 Windows Credential Manager. |
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 credential subcommands select one local profile:
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 <profile> [--secret-stdin]
820
- arcane mail key status <profile>
821
- arcane mail key delete <profile>
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 sent to the Windows Credential Manager
827
- helper over child-process stdin, never argv, and no plaintext fallback is
828
- created. Status reports only whether the profile exists. Delete returns the
829
- selected profile with `exists:false`; it intentionally does not distinguish a
830
- new deletion from an already-absent profile. Non-Windows hosts report the
831
- credential operation as unavailable.
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 Windows Credential Manager profile; neither it nor report content is
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 credential profile supplies only the server-side Resend API key.
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
@@ -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 Windows Credential Manager. |
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. Non-Windows hosts report credential operations as unavailable;
21
- there is no plaintext fallback.
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 Windows
46
- Credential Manager adapter. Programmatic developer tooling reaches those
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. `arcane mail key set <profile>`
57
- stores it in Windows Credential Manager. `mail send --profile <profile>` and
58
- `mail serve --profile <profile>` read it only inside the owning Node process.
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
- Store one Resend key under a local profile:
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 arcane-dev
277
- arcane mail key status arcane-dev
278
- arcane mail key delete arcane-dev
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 --profile arcane-dev --from "Arcane <verified@example.com>" --report-key <stable-id> --report-stdin
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 arcane-dev --from "Arcane <verified@example.com>"
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 method and action review is complete, and the inspected gateway implements the removals and simplifications below. Subscription verification now has a public optional callback: an omitted callback leaves the initial setup operational with `callerAuthentication: 'none'`; a configured callback selects `'subscription'` and controls admission before any provider attempt. The real TWiNStripeServer endpoint adapter comes later and does not block this delivery. Version `0.16.0` is selected; commit, package, and publication outcomes must be reported by the delivery owner when they actually occur.
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. No runtime speedup, successful live mail delivery, completed source release, or subscription acceptance is claimed.
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-manager operations and the provider API key separate. |
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 now accept optional `--from`; Resend owns sender validity. The chosen credential profile and stable send key remain required. |
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 | Version `0.16.0` selected; root owns final source/documentation reconciliation and the actual commit/package/publication outcome. | This report claims no completed source release, runtime test, live send, or remote subscription verification. The pending remote adapter does not hold the current increment. |
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcane-os",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "Arcane OS JavaScript SDK, project-local CLI, browser runtime, and repository-portable application packager.",
5
5
  "type": "module",
6
6
  "main": "./src/index.mjs",
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 <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>]
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)||!profile){
662
- usage('mail key requires set, status, or delete followed by one profile id.');
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
- profile:values.profile,
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
- for(const [name,value]of Object.entries({
714
- profile:values.profile,
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
- profile:values.profile,
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 <profile>, send, or serve.');
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
  }