Skip to content

Commit fde3e32

Browse files
committed
Merge tag '2.0.12' into 2.1-maintenance
Fedify 2.0.12
2 parents bae24e1 + 2fc3442 commit fde3e32

8 files changed

Lines changed: 359 additions & 52 deletions

File tree

CHANGES.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,37 @@ Version 2.1.5
88

99
To be released.
1010

11+
### @fedify/fedify
12+
13+
- Fixed `Context.getActorKeyPairs()` assigning the same key ID to both
14+
the `CryptographicKey` (used for HTTP Signatures and Linked Data
15+
Signatures) and the `Multikey` (used for Object Integrity Proofs) within
16+
an `ActorKeyPair`. The `Multikey` now receives a distinct ID
17+
(`#multikey-1`, `#multikey-2`, …) so that the actor document no longer
18+
contains two objects sharing the same `id`, which was invalid JSON-LD.
19+
Object Integrity Proof signatures now reference the correct `Multikey` ID
20+
instead of the `CryptographicKey` ID. [[#663]]
21+
22+
- Object Integrity Proofs signing now takes place before activity fanout,
23+
so all recipients receive the same pre-signed activity. Previously, OIP
24+
signing was deferred until after fanout, meaning each fanout worker would
25+
re-sign independently with potentially different timestamps and the fanout
26+
message itself contained an unsigned activity.
27+
28+
[#663]: https://github.com/fedify-dev/fedify/issues/663
29+
30+
### @fedify/cfworkers
31+
32+
- Fixed a remaining TypeScript type mismatch for Cloudflare Workers users who
33+
pass `wrangler types` or `@cloudflare/vite-plugin` generated KV bindings to
34+
`WorkersKvStore`. The package now accepts a minimal structural KV binding
35+
interface for `WorkersKvStore` and `WorkersMessageQueue`'s `orderingKv`
36+
option instead of requiring the nominal `KVNamespace` type imported from
37+
`@cloudflare/workers-types`, so generated local declarations compile
38+
without casts or `@ts-expect-error`. [[#665]]
39+
40+
[#665]: https://github.com/fedify-dev/fedify/issues/665
41+
1142

1243
Version 2.1.4
1344
-------------
@@ -300,6 +331,39 @@ Released on March 24, 2026.
300331
[#599]: https://github.com/fedify-dev/fedify/pull/599
301332

302333

334+
Version 2.0.12
335+
--------------
336+
337+
Released on April 8, 2026.
338+
339+
### @fedify/fedify
340+
341+
- Fixed `Context.getActorKeyPairs()` assigning the same key ID to both
342+
the `CryptographicKey` (used for HTTP Signatures and Linked Data
343+
Signatures) and the `Multikey` (used for Object Integrity Proofs) within
344+
an `ActorKeyPair`. The `Multikey` now receives a distinct ID
345+
(`#multikey-1`, `#multikey-2`, …) so that the actor document no longer
346+
contains two objects sharing the same `id`, which was invalid JSON-LD.
347+
Object Integrity Proof signatures now reference the correct `Multikey` ID
348+
instead of the `CryptographicKey` ID. [[#663]]
349+
350+
- Object Integrity Proofs signing now takes place before activity fanout,
351+
so all recipients receive the same pre-signed activity. Previously, OIP
352+
signing was deferred until after fanout, meaning each fanout worker would
353+
re-sign independently with potentially different timestamps and the fanout
354+
message itself contained an unsigned activity.
355+
356+
### @fedify/cfworkers
357+
358+
- Fixed a remaining TypeScript type mismatch for Cloudflare Workers users who
359+
pass `wrangler types` or `@cloudflare/vite-plugin` generated KV bindings to
360+
`WorkersKvStore`. The package now accepts a minimal structural KV binding
361+
interface for `WorkersKvStore` and `WorkersMessageQueue`'s `orderingKv`
362+
option instead of requiring the nominal `KVNamespace` type imported from
363+
`@cloudflare/workers-types`, so generated local declarations compile
364+
without casts or `@ts-expect-error`. [[#665]]
365+
366+
303367
Version 2.0.11
304368
--------------
305369

@@ -1102,6 +1166,29 @@ Released on February 22, 2026.
11021166
[#351]: https://github.com/fedify-dev/fedify/issues/351
11031167

11041168

1169+
Version 1.10.8
1170+
--------------
1171+
1172+
Released on April 8, 2026.
1173+
1174+
### @fedify/fedify
1175+
1176+
- Fixed `Context.getActorKeyPairs()` assigning the same key ID to both
1177+
the `CryptographicKey` (used for HTTP Signatures and Linked Data
1178+
Signatures) and the `Multikey` (used for Object Integrity Proofs) within
1179+
an `ActorKeyPair`. The `Multikey` now receives a distinct ID
1180+
(`#multikey-1`, `#multikey-2`, …) so that the actor document no longer
1181+
contains two objects sharing the same `id`, which was invalid JSON-LD.
1182+
Object Integrity Proof signatures now reference the correct `Multikey` ID
1183+
instead of the `CryptographicKey` ID. [[#663]]
1184+
1185+
- Object Integrity Proofs signing now takes place before activity fanout,
1186+
so all recipients receive the same pre-signed activity. Previously, OIP
1187+
signing was deferred until after fanout, meaning each fanout worker would
1188+
re-sign independently with potentially different timestamps and the fanout
1189+
message itself contained an unsigned activity.
1190+
1191+
11051192
Version 1.10.7
11061193
--------------
11071194

@@ -1302,6 +1389,29 @@ Released on December 24, 2025.
13021389
- Implemented `list()` method in `WorkersKvStore`. [[#498], [#500]]
13031390

13041391

1392+
Version 1.9.9
1393+
-------------
1394+
1395+
Released on April 8, 2026.
1396+
1397+
### @fedify/fedify
1398+
1399+
- Fixed `Context.getActorKeyPairs()` assigning the same key ID to both
1400+
the `CryptographicKey` (used for HTTP Signatures and Linked Data
1401+
Signatures) and the `Multikey` (used for Object Integrity Proofs) within
1402+
an `ActorKeyPair`. The `Multikey` now receives a distinct ID
1403+
(`#multikey-1`, `#multikey-2`, …) so that the actor document no longer
1404+
contains two objects sharing the same `id`, which was invalid JSON-LD.
1405+
Object Integrity Proof signatures now reference the correct `Multikey` ID
1406+
instead of the `CryptographicKey` ID. [[#663]]
1407+
1408+
- Object Integrity Proofs signing now takes place before activity fanout,
1409+
so all recipients receive the same pre-signed activity. Previously, OIP
1410+
signing was deferred until after fanout, meaning each fanout worker would
1411+
re-sign independently with potentially different timestamps and the fanout
1412+
message itself contained an unsigned activity.
1413+
1414+
13051415
Version 1.9.8
13061416
-------------
13071417

packages/cfworkers/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
"build": "pnpm --filter @fedify/cfworkers... run build:self",
6767
"prepack": "pnpm build",
6868
"prepublish": "pnpm build",
69-
"test": "vitest run"
69+
"pretest": "pnpm build",
70+
"test": "tsc -p test/typecheck/tsconfig.json --noEmit && vitest run"
7071
}
7172
}

packages/cfworkers/src/mod.ts

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,7 @@
88
* @module
99
* @since 1.9.0
1010
*/
11-
import type {
12-
KVNamespace,
13-
MessageSendRequest,
14-
Queue,
15-
} from "@cloudflare/workers-types";
11+
import type { MessageSendRequest, Queue } from "@cloudflare/workers-types";
1612
import type {
1713
KvKey,
1814
KvStore,
@@ -27,6 +23,67 @@ interface KvMetadata {
2723
expires?: number;
2824
}
2925

26+
interface WorkersKvNamespaceGetWithMetadataResult<Value, Metadata> {
27+
readonly value: Value | null;
28+
readonly metadata: Metadata | null;
29+
}
30+
31+
interface WorkersKvNamespaceListKey<Metadata, Key extends string = string> {
32+
readonly name: Key;
33+
readonly expiration?: number;
34+
readonly metadata?: Metadata;
35+
}
36+
37+
interface WorkersKvNamespaceListResult<Metadata, Key extends string = string> {
38+
readonly list_complete: boolean;
39+
readonly keys: readonly WorkersKvNamespaceListKey<Metadata, Key>[];
40+
readonly cursor?: string;
41+
}
42+
43+
interface WorkersKvNamespaceListOptions {
44+
readonly limit?: number;
45+
readonly prefix?: string | null;
46+
readonly cursor?: string | null;
47+
}
48+
49+
interface WorkersKvNamespacePutOptions {
50+
readonly expiration?: number;
51+
readonly expirationTtl?: number;
52+
readonly metadata?: unknown;
53+
}
54+
55+
/**
56+
* Minimal Cloudflare Workers KV binding shape used by this package.
57+
* Compatible with both `@cloudflare/workers-types` and `wrangler types`
58+
* generated declarations.
59+
* @since 2.0.12
60+
*/
61+
export interface WorkersKvNamespaceLike<Key extends string = string> {
62+
get(key: Key): Promise<string | null>;
63+
get<ExpectedValue = unknown>(
64+
key: Key,
65+
type: "json",
66+
): Promise<ExpectedValue | null>;
67+
getWithMetadata<Metadata = unknown>(
68+
key: Key,
69+
): Promise<WorkersKvNamespaceGetWithMetadataResult<string, Metadata>>;
70+
getWithMetadata<ExpectedValue = unknown, Metadata = unknown>(
71+
key: Key,
72+
type: "json",
73+
): Promise<
74+
WorkersKvNamespaceGetWithMetadataResult<ExpectedValue, Metadata>
75+
>;
76+
put(
77+
key: Key,
78+
value: string,
79+
options?: WorkersKvNamespacePutOptions,
80+
): Promise<void>;
81+
delete(key: Key): Promise<void>;
82+
list<Metadata = unknown>(
83+
options?: WorkersKvNamespaceListOptions,
84+
): Promise<WorkersKvNamespaceListResult<Metadata, Key>>;
85+
}
86+
3087
/**
3188
* Internal message wrapper that includes ordering key metadata.
3289
*/
@@ -74,9 +131,9 @@ export interface ProcessMessageResult {
74131
* @since 1.9.0
75132
*/
76133
export class WorkersKvStore implements KvStore {
77-
#namespace: KVNamespace<string>;
134+
#namespace: WorkersKvNamespaceLike<string>;
78135

79-
constructor(namespace: KVNamespace<string>) {
136+
constructor(namespace: WorkersKvNamespaceLike<string>) {
80137
this.#namespace = namespace;
81138
}
82139

@@ -202,7 +259,7 @@ export interface WorkersMessageQueueOptions {
202259
* guarantees are best-effort. For strict ordering requirements, consider
203260
* using Durable Objects.
204261
*/
205-
readonly orderingKv?: KVNamespace<string>;
262+
readonly orderingKv?: WorkersKvNamespaceLike<string>;
206263

207264
/**
208265
* The prefix for ordering key lock keys. Defaults to `"__fedify_ordering_"`.
@@ -233,7 +290,7 @@ export interface WorkersMessageQueueOptions {
233290
*/
234291
export class WorkersMessageQueue implements MessageQueue {
235292
#queue: Queue;
236-
#orderingKv?: KVNamespace<string>;
293+
#orderingKv?: WorkersKvNamespaceLike<string>;
237294
#orderingKeyPrefix: string;
238295
#orderingLockTtl: number;
239296

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ESNext",
4+
"module": "ESNext",
5+
"moduleResolution": "bundler",
6+
"strict": true,
7+
"esModuleInterop": true,
8+
"skipLibCheck": true
9+
},
10+
"include": [
11+
"./**/*.ts"
12+
]
13+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import type { Queue } from "@cloudflare/workers-types";
2+
import { WorkersKvStore, WorkersMessageQueue } from "../../dist/mod.js";
3+
4+
interface GeneratedKvGetWithMetadataResult<Value, Metadata> {
5+
readonly value: Value | null;
6+
readonly metadata: Metadata | null;
7+
readonly cacheStatus: string | null;
8+
}
9+
10+
interface GeneratedKvListKey<Metadata, Key extends string = string> {
11+
readonly name: Key;
12+
readonly expiration?: number;
13+
readonly metadata?: Metadata;
14+
}
15+
16+
type GeneratedKvListResult<Metadata, Key extends string = string> =
17+
| {
18+
readonly list_complete: false;
19+
readonly keys: readonly GeneratedKvListKey<Metadata, Key>[];
20+
readonly cursor: string;
21+
readonly cacheStatus: string | null;
22+
}
23+
| {
24+
readonly list_complete: true;
25+
readonly keys: readonly GeneratedKvListKey<Metadata, Key>[];
26+
readonly cacheStatus: string | null;
27+
};
28+
29+
/**
30+
* Mirrors the minimal single-key Cloudflare KV declaration shape emitted by
31+
* `wrangler types`, but comes from a distinct local declaration source.
32+
*/
33+
interface GeneratedKvNamespace<Key extends string = string> {
34+
get(key: Key): Promise<string | null>;
35+
get<ExpectedValue = unknown>(
36+
key: Key,
37+
type: "json",
38+
): Promise<ExpectedValue | null>;
39+
getWithMetadata<Metadata = unknown>(
40+
key: Key,
41+
): Promise<GeneratedKvGetWithMetadataResult<string, Metadata>>;
42+
getWithMetadata<ExpectedValue = unknown, Metadata = unknown>(
43+
key: Key,
44+
type: "json",
45+
): Promise<GeneratedKvGetWithMetadataResult<ExpectedValue, Metadata>>;
46+
put(
47+
key: Key,
48+
value: string | ArrayBuffer | ArrayBufferView | ReadableStream,
49+
options?: {
50+
expiration?: number;
51+
expirationTtl?: number;
52+
metadata?: any | null;
53+
},
54+
): Promise<void>;
55+
delete(key: Key): Promise<void>;
56+
list<Metadata = unknown>(
57+
options?: {
58+
limit?: number;
59+
prefix?: string | null;
60+
cursor?: string | null;
61+
},
62+
): Promise<GeneratedKvListResult<Metadata, Key>>;
63+
}
64+
65+
declare const queue: Queue;
66+
declare const generatedKv: GeneratedKvNamespace<string>;
67+
68+
new WorkersKvStore(generatedKv);
69+
new WorkersMessageQueue(queue, { orderingKv: generatedKv });

packages/fedify/src/federation/context.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -876,7 +876,10 @@ export interface GetSignedKeyOptions {
876876
*/
877877
export interface ActorKeyPair extends CryptoKeyPair {
878878
/**
879-
* The URI of the public key, which is used for verifying HTTP Signatures.
879+
* The URI of the public key for {@link CryptographicKey}, which is used for
880+
* verifying HTTP Signatures and Linked Data Signatures. Note that this is
881+
* the ID of the {@link cryptographicKey}, not of the {@link multikey};
882+
* the {@link Multikey} instance has a distinct ID of its own.
880883
*/
881884
readonly keyId: URL;
882885

0 commit comments

Comments
 (0)