@festapp/banksync 0.0.0-bootstrap.20260831

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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Festapp
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # BankSync
2
+
3
+ BankSync turns bank transaction notifications into durable, signed webhooks.
4
+ It is a TypeScript package and a deployable Cloudflare Worker maintained by
5
+ [Festapp](https://github.com/festappnet).
6
+
7
+ The included Worker supports:
8
+
9
+ - Fio Bank notification email and Fio API ingestion;
10
+ - Air Bank notification email ingestion;
11
+ - MIME parsing, sender allowlisting and per-account pairing addresses;
12
+ - layered transaction deduplication in Cloudflare D1;
13
+ - durable webhook delivery through Cloudflare Queues with retry and healing;
14
+ - encrypted webhook secrets, audit records, retention and optional R2 backups.
15
+
16
+ ## Install as a package
17
+
18
+ `v0.1.0` is an unsafe preview and must not be used in production. Install only
19
+ the latest version published to npm by the protected release workflow after its
20
+ checksum and provenance are available:
21
+
22
+ ```bash
23
+ pnpm add @festapp/banksync
24
+ ```
25
+
26
+ The root export contains runtime-neutral parsing and webhook helpers:
27
+
28
+ ```ts
29
+ import {
30
+ detectProvider,
31
+ parseEmail,
32
+ verifyWebhook,
33
+ type WebhookEnvelope,
34
+ } from "@festapp/banksync";
35
+ ```
36
+
37
+ The Cloudflare Worker is a separate export so importing the core does not pull
38
+ Cloudflare bindings into application code:
39
+
40
+ ```ts
41
+ export { default } from "@festapp/banksync/cloudflare";
42
+ export type { Env } from "@festapp/banksync/cloudflare";
43
+ ```
44
+
45
+ ## Deploy to Cloudflare
46
+
47
+ 1. Clone this repository and install dependencies with `pnpm install`.
48
+ 2. Copy `wrangler.example.toml` to `wrangler.toml` and replace every placeholder.
49
+ 3. Create the D1 database, queues and optional R2 bucket named in the config.
50
+ 4. For a fresh database apply the baseline; for an existing production database
51
+ whose history ends at `0009`, apply only `0010_security_hardening.sql` through
52
+ a scoped reviewed migration operation.
53
+ 5. Set `ADMIN_SECRET`, `WEBHOOK_KEK`, `ENCRYPTION_KEY_V1`, and the active
54
+ `BACKUP_ENCRYPTION_KEY_Vn` with
55
+ `pnpm wrangler secret put <NAME>`.
56
+ 6. Configure `CALLBACK_HOST_ALLOWLIST` with exact consumer hostnames. Production
57
+ refuses an empty allowlist, credentials, IP literals, special-use hosts,
58
+ fragments, suffix matches, and redirects.
59
+ 7. Inspect one sanitized accepted and rejected Cloudflare message to establish
60
+ the Cloudflare-owned Authentication-Results authserv-id, then configure it as
61
+ `EMAIL_AUTHSERV_ID`. Email ingest intentionally remains disabled while it is
62
+ absent; MIME headers are never an identity fallback.
63
+ 8. Run the release gates and deploy the exact attested artifact.
64
+
65
+ All three key families must be independent random 32-byte base64 values.
66
+ `BACKUP_ENCRYPTION_KEY_VERSION` selects the active backup key. Store backup
67
+ keys outside R2 and test restore before rotation. Never commit `wrangler.toml`,
68
+ `.dev.vars`, bank API tokens, backup keys, or consumer webhook secrets.
69
+
70
+ ### Migration baseline
71
+
72
+ Fresh databases are created from the single canonical schema baseline
73
+ `migrations/0001_schema.sql` (schema version 10). The original incremental
74
+ `0001`–`0009` history remains recorded in existing D1 databases but is not
75
+ needed to create a new deployment. To stay compatible with those databases,
76
+ production upgrade is `0010_security_hardening.sql`; all future migrations must
77
+ start at `0011` or higher. Never renumber
78
+ or replace the baseline filename.
79
+
80
+ BankSync's D1 schema is intentionally independent of any consumer's billing
81
+ database. Billing settlement belongs behind each consumer's signed webhook
82
+ endpoint, so installing BankSync never creates or changes Mendelio/Supabase
83
+ billing tables or routines.
84
+
85
+ For local development, copy `.dev.vars.example` to `.dev.vars`, use placeholder
86
+ Cloudflare resource identifiers in `wrangler.toml`, apply migrations locally,
87
+ and run `pnpm dev`.
88
+
89
+ ## Webhook contract
90
+
91
+ BankSync sends `transaction.received` version `1`. `delivery_id` is stable
92
+ across retries and is the consumer idempotency key. Each request includes:
93
+
94
+ - `X-BankSync-Timestamp`
95
+ - `X-BankSync-Delivery-Id`
96
+ - `X-BankSync-Signature: sha256=<hex>`
97
+
98
+ The HMAC input is the exact byte sequence
99
+ `timestamp + "." + deliveryId + "." + bodyBytes`. Verify the raw body before
100
+ JSON parsing. `verifyWebhook` validates raw-body HMAC, timestamp syntax and
101
+ tolerance, delivery header/body equality, event, and event version, then returns
102
+ a typed verified envelope. It throws `WebhookVerificationError` with a stable
103
+ code. There is intentionally no HMAC-only public verifier.
104
+
105
+ ```ts
106
+ import { verifyWebhook } from "@festapp/banksync";
107
+
108
+ const bodyBytes = new Uint8Array(await request.arrayBuffer());
109
+ const envelope = await verifyWebhook({
110
+ secret,
111
+ timestamp: request.headers.get("x-banksync-timestamp") ?? "",
112
+ deliveryId: request.headers.get("x-banksync-delivery-id") ?? "",
113
+ signature: request.headers.get("x-banksync-signature") ?? "",
114
+ bodyBytes,
115
+ });
116
+ // Atomically claim envelope.delivery_id in durable consumer storage before any
117
+ // billing or other side effect. The verifier cannot provide durable replay
118
+ // protection on its own.
119
+ ```
120
+
121
+ ## Trust boundaries and operations
122
+
123
+ - Cloudflare Email Routing is the trusted SMTP/envelope boundary. The Worker
124
+ requires one configured trusted auth result plus aligned DKIM or DMARC and
125
+ exact agreement between envelope and MIME identities.
126
+ - `bank_accounts.owner_app_id` is the tenant authorization authority. Only an
127
+ admin can create a cross-owner subscription.
128
+ - Public HTTP exposes only `GET /health` with `{ "ok": true }` or a stable
129
+ unavailable response. `/status` and `/health/deep` require admin auth.
130
+ - Callback delivery revalidates the current exact-host policy immediately before
131
+ every fetch and never follows redirects.
132
+ - Idempotency is scoped by principal, method, and canonical path. Credential
133
+ creation/rotation endpoints reject `Idempotency-Key` and never cache responses.
134
+ - R2 backups are AES-256-GCM `.sql.enc` envelopes. Ephemeral idempotency and
135
+ rate-limit tables are excluded. Decrypt to a new mode-0600 file without
136
+ printing plaintext:
137
+
138
+ ```bash
139
+ BACKUP_DECRYPTION_KEY='<base64 key>' \
140
+ node scripts/decrypt-backup.mjs backup.sql.enc restored.sql
141
+ ```
142
+
143
+ ## Development
144
+
145
+ ```bash
146
+ pnpm check
147
+ pnpm pack --dry-run
148
+ ```
149
+
150
+ Provider-live tests are intentionally not part of the default suite and must
151
+ never be run with production bank credentials.
152
+
153
+ ## License
154
+
155
+ MIT