sigflint 0.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,24 @@
1
+ # Changelog
2
+
3
+ ## 1.0.1 - 2026-10-18
4
+
5
+ - A lowercase `method` (`get`) is signed as upper case (`GET`), as botocore does. `fetch` sends `get`, `put`, `post`, `delete`, `head` and `options` in upper case, so before this the signature did not match and the service answered `SignatureDoesNotMatch`. Node's `fetch` sends `patch` as written, so pass `PATCH` in upper case to both sigflint and `fetch`.
6
+ - `end(trailers)` on a chunk signer throws a `TypeError` when a trailer name or value holds a line break. Before, the break was signed as another trailer line.
7
+ - Correction to the 1.0.0 entry: a raw `+` in an S3 path is signed as a space, but "as S3 reads it" was not verified. Nothing has been tried against AWS S3, and a local `s3s-fs` 0.17.0 server reads a raw `+` as a plus sign and rejected the signature. Write `%2B` in the URL to mean a plus: that server accepted it from sigflint and from botocore. The signing is unchanged (`docs/guide.md`).
8
+ - Correction to the 1.0.0 entry: a bundler that picks the `node` or `bun` condition gets the built file (`dist/`) and drops unused signers just as well; the per-signer modules (`src/`) go to the `workerd`, `worker` and `browser` conditions and to bundlers that match none of them.
9
+
10
+ ## 1.0.0 - 2026-10-16
11
+
12
+ First version: AWS Signature Version 4 signing for every AWS service.
13
+
14
+ - `getAuthHeaders(request, credentials)` returns the headers to send, for S3 and, with `request.service`, any other service. It never modifies the request or its URL.
15
+ - `presign(request, credentials)` returns a presigned URL.
16
+ - `getAuthHeadersV4a` and `presignV4a` sign with SigV4a (ECDSA P-256) for the regions in `request.regionSet`.
17
+ - `chunkSigner(authHeaders, credentials)` signs aws-chunked uploads, with signed trailing headers; `eventSigner(authHeaders, credentials)` signs event-stream messages.
18
+ - Session tokens (`credentials.sessionToken`) and unsigned payloads (`request.unsignedPayload`).
19
+ - Path and query encoding, query sorting and header trimming follow the specification; a raw `+` in an S3 path is a space, as S3 reads it. Other services normalise the path and encode it twice.
20
+ - Input that cannot be signed correctly throws: `signHeaders` that name `x-amz-date`, `authorization` or `__proto__` or hold a line break, a presigned URL that is not http or https or is already presigned, an `expires` outside 1 to 604800 seconds, and the headers of a SigV4a signature given to `chunkSigner` or `eventSigner`. An empty chunk is not signed, because it would end the upload.
21
+ - No dependencies. Node.js 22.12 and later, Bun, Deno and Cloudflare Workers; the signing-key cache is shared by an entry's signers.
22
+ - Each signer is its own module and export, so a bundle holds only what it imports. Node and Bun load one built file (`dist/`) instead of six modules; the `workerd`, `worker` and `browser` conditions and bundlers keep the per-signer modules.
23
+ - Cloudflare Workers and browsers get a WebCrypto implementation through the `workerd`, `worker` and `browser` export conditions, with or without `nodejs_compat`. There every signer returns a promise (the chunk and event signers' methods do); the types are `x | Promise<x>`, so always `await`. `sigflint/web` imports that implementation directly.
24
+ - Speed, memory and size are measured against six other libraries (`docs/benchmarks.md`, `bench/`).
package/README.md CHANGED
@@ -1,5 +1,115 @@
1
- # sigflint
1
+ <h1 align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/banner-dark.png">
4
+ <img src="docs/assets/banner-light.png" alt="sigflint: Fast SigV4 and SigV4a signing for every AWS service" width="100%">
5
+ </picture>
6
+ </h1>
2
7
 
3
- This version only reserves the package name. It contains no code. sigflint 1.0.0 follows.
8
+ <p align="center">
9
+ <a href="https://github.com/bompus/sigflint/actions/workflows/ci.yml"><img src="https://github.com/bompus/sigflint/actions/workflows/ci.yml/badge.svg" alt="CI status"></a>
10
+ <a href="https://github.com/bompus/sigflint/releases/latest"><img src="https://badgen.net/github/release/bompus/sigflint" alt="Latest GitHub release"></a>
11
+ <a href="https://www.npmjs.com/package/sigflint"><img src="https://badgen.net/npm/v/sigflint" alt="Latest sigflint version on npm"></a>
12
+ <a href="LICENSE"><img src="https://badgen.net/badge/license/MIT/blue" alt="MIT license"></a>
13
+ </p>
4
14
 
5
- Source: https://github.com/bompus/sigflint
15
+ <p align="center">
16
+ <a href="docs/guide.md#usage">Headers and presigned URLs</a>&nbsp;·
17
+ <a href="docs/guide.md#sigv4a-multi-region">SigV4a</a>&nbsp;·
18
+ <a href="docs/guide.md#chunked-uploads">Chunked uploads and event streams</a>&nbsp;·
19
+ <a href="package.json">No dependencies</a>
20
+ </p>
21
+
22
+ Fast AWS Signature Version 4 request signing for every AWS service, with no dependencies. It runs on Node.js, Bun, Deno, Cloudflare Workers and browsers.
23
+
24
+ ```js
25
+ import { getAuthHeaders } from 'sigflint';
26
+
27
+ const url = new URL('https://my-bucket.s3.us-east-1.amazonaws.com/photos/cat.jpg');
28
+ const headers = await getAuthHeaders(
29
+ { method: 'GET', url, region: 'us-east-1' },
30
+ { accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY },
31
+ );
32
+ const response = await fetch(url, { headers });
33
+ ```
34
+
35
+ It signs requests with the `Authorization` header or as a presigned URL, for S3 and for any other service (`service: 'dynamodb'`, `'sqs'`, `'execute-api'`, ...), and it also does multi-region signatures (SigV4a), chunked uploads and event streams.
36
+
37
+ - **Correct.** It returns the same signature as botocore for all 3,360 S3 requests in its test corpus, and for 1,622 requests to other services and 1,600 presigned URLs. For S3, botocore signs the request after this repository's own path and query normalization (`conformance/oracle.py`), so that step is not checked independently by the corpus; the four S3 examples published by AWS, which are in it, are. SigV4a signatures verify against the AWS CRT's, and every chunk and event signature in the test corpus matches the AWS SDK's. See [how it is tested](docs/benchmarks.md#how-it-is-tested).
38
+ - **Fast.** 1.3 to 3.6 µs per header or presigned-URL signature on Node and 1.4 to 5.0 µs on Bun. The other libraries take 2.6 to 552 times as long on the requests they sign correctly (five Bun presigned-URL comparisons, with `aws4`, `@smithy/signature-v4` and `minio`, are not clear and are left out). See [speed](docs/benchmarks.md#speed).
39
+ - **Small, and you pay only for what you import.** Signing headers is 5.7 kB minified, 2.8 kB gzipped; presigned URLs, SigV4a, chunked uploads and event streams each add to a bundle only when imported. Nothing to install with it. See [size](docs/benchmarks.md#size).
40
+ - **Everywhere.** One import on Node, Bun, Deno and Cloudflare Workers, with or without `nodejs_compat`. See [runtimes](#runtimes).
41
+
42
+ ## Install
43
+
44
+ ```sh
45
+ npm install sigflint
46
+ ```
47
+
48
+ Node.js 22.12 or newer (it is an ES module; Node 22.12 can also `require()` it). Bun and Deno need no setup.
49
+
50
+ ## Usage
51
+
52
+ Sign a request and send the returned headers with it. The `host` header is signed but not returned; your HTTP client sets it from the URL.
53
+
54
+ ```js
55
+ import { getAuthHeaders } from 'sigflint';
56
+
57
+ const credentials = { accessKeyId, secretAccessKey, sessionToken }; // sessionToken is optional
58
+
59
+ // upload an object: the body is hashed into x-amz-content-sha256
60
+ const url = new URL('https://my-bucket.s3.us-east-1.amazonaws.com/notes/today.json');
61
+ const body = JSON.stringify({ done: true });
62
+ const headers = await getAuthHeaders(
63
+ { method: 'PUT', url, region: 'us-east-1', body, signHeaders: { 'content-type': 'application/json' } },
64
+ credentials,
65
+ );
66
+ await fetch(url, { method: 'PUT', headers, body });
67
+ ```
68
+
69
+ For a large or binary body, skip hashing and send `unsignedPayload: true`. S3 accepts that over HTTPS.
70
+
71
+ ```js
72
+ const headers = await getAuthHeaders({ method: 'PUT', url, region, unsignedPayload: true }, credentials);
73
+ ```
74
+
75
+ For another S3-compatible service, pass its region name and URL (`https://us-sea-1.linodeobjects.com/bucket/key`, `http://127.0.0.1:9000/bucket/key`). These are examples of the form, not endpoints that were tried. Signatures are checked against botocore, the AWS CRT, the AWS SDK's signer and the AWS documentation examples. Nothing has been sent to AWS S3, so a service that canonicalizes requests differently from S3 may reject them. The only server tried is a local `s3s-fs` 0.17.0 test server on loopback, once, with header-signed requests (create a bucket, `PUT`, `GET`, list): it accepted them, except that it rejected sigflint's signature for a raw `+` in the path, so write `%2B` ([how the path is read](docs/guide.md#how-it-reads-the-url)).
76
+
77
+ More in the [guide](docs/guide.md): [other services](docs/guide.md#other-services), [presigned URLs](docs/guide.md#presigned-urls), [SigV4a](docs/guide.md#sigv4a-multi-region), [chunked uploads](docs/guide.md#chunked-uploads), [event streams](docs/guide.md#event-streams), why to always `await`, and the [full API](docs/guide.md#api).
78
+
79
+ ## Runtimes
80
+
81
+ Node.js 22.12 and later, Bun, Deno, and Cloudflare Workers with or without `nodejs_compat`; browsers get the same WebCrypto code. Each runtime was checked against botocore's signature for one S3 request. The grid, with the other libraries, is in the [benchmarks](docs/benchmarks.md#runtimes).
82
+
83
+ ## Benchmarks
84
+
85
+ <picture>
86
+ <source media="(prefers-color-scheme: dark)" srcset="docs/speed-dark.svg">
87
+ <img alt="Bar chart: time in microseconds to sign one S3 request on Node.js and Bun, for sigflint and six other libraries" src="docs/speed-light.svg">
88
+ </picture>
89
+
90
+ Correctness, [speed](docs/benchmarks.md#speed), [memory](docs/benchmarks.md#memory) and [size](docs/benchmarks.md#size), the [test corpora](docs/benchmarks.md#how-it-is-tested) and the [methodology and limits](docs/benchmarks.md#methodology-and-limits) are in [docs/benchmarks.md](docs/benchmarks.md). Every figure there comes from raw results committed in `bench/results/`. The speed, memory and import figures were measured on the code in this release and are re-run before each release.
91
+
92
+ ## Scope
93
+
94
+ 1.0 signs requests for every AWS service in header form and as presigned URLs, plus SigV4a, chunked uploads and event streams. Not included, and not promised:
95
+
96
+ - hashing binary request bodies for you: hash them yourself, or use `unsignedPayload`
97
+ - an HTTP client, retries, credential providers or region lookup
98
+ - anything that is not SigV4 or SigV4a
99
+
100
+ Open an issue describing the use case if you need one of them.
101
+
102
+ ## Security
103
+
104
+ See [SECURITY.md](SECURITY.md). sigflint never logs credentials or writes them anywhere. Its signing-key caches keep secret keys in process memory: each cached signing key (up to 1,000 per cache) is stored under a lookup string that contains the secret access key it was derived from.
105
+
106
+ ## Sponsoring
107
+
108
+ sigflint is built and maintained by one person. If it saves you time, you can
109
+ sponsor it monthly or once through
110
+ [GitHub Sponsors](https://github.com/sponsors/bompus), or leave a tip on
111
+ [Ko-fi](https://ko-fi.com/bompus).
112
+
113
+ ## License
114
+
115
+ [MIT](LICENSE). `CODE_OF_CONDUCT.md` is the Contributor Covenant under CC BY 4.0; see `THIRD_PARTY_NOTICES.md`.