1---
2title: 2.0.0-beta.46 - Vectorize
3date: 2026-05-31T08:00:00Z
4category: release
5excerpt: v2.0.0-beta.46 adds Cloudflare Vectorize — a bindable vector-database index plus metadata indexes for filtered queries — and fixes a state-store bootstrap bug that was silently rotating your encryption key and auth token on every update.
6---
7
8`v2.0.0-beta.46` ships **Cloudflare Vectorize** as a bindable
9resource — index, metadata indexes, and an Effect-native runtime
10client — plus an important fix to how the Cloudflare state store
11bootstraps.
12
13## `Cloudflare.VectorizeIndex`
14
15A `VectorizeIndex` is a globally distributed vector database
16index. Declare one as a resource, bind it to a Worker, and you
17get a typed Effect-native client for inserting and querying
18vector embeddings.
19
20```typescript
21const index = yield* Cloudflare.VectorizeIndex("docs", {
22 dimensions: 768,
23 metric: "cosine",
24});
25```
26
27Or pin `dimensions` and `metric` to a managed embedding model
28with a `preset`:
29
30```typescript
31const index = yield* Cloudflare.VectorizeIndex("docs", {
32 preset: "@cf/baai/bge-base-en-v1.5",
33});
34```
35
36The index is **immutable** — `dimensions`, `metric`, `preset`,
37and `description` are all fixed at creation, so changing any of
38them triggers a replacement.
39
40## Bind it to a Worker
41
42`Cloudflare.VectorizeIndex.bind(index)` attaches the binding and
43hands back a client whose methods — `upsert`, `query`,
44`queryById`, `insert`, `deleteByIds`, `getByIds`, `describe` —
45are all `yield*`-able Effects:
46
47```typescript
48export default class Worker extends Cloudflare.Worker<Worker>()(
49 "Worker",
50 { main: import.meta.filename },
51 Effect.gen(function* () {
52 const docs = yield* Cloudflare.VectorizeIndex.bind(index);
53
54 return {
55 fetch: Effect.gen(function* () {
56 yield* docs.upsert([
57 { id: "1", values: [0.1, 0.2, 0.3], metadata: { kind: "doc" } },
58 ]);
59 const matches = yield* docs.query([0.1, 0.2, 0.3], { topK: 5 });
60 return yield* HttpServerResponse.json({ count: matches.count });
61 }),
62 };
63 }).pipe(Effect.provide(Cloudflare.VectorizeIndexBindingLive)),
64) {}
65```
66
67## `Cloudflare.VectorizeMetadataIndex`
68
69By default you can't `filter` a query on a metadata property —
70the property has to be indexed first. `VectorizeMetadataIndex`
71declares that index. Point it at a parent index, name the
72property, and give its type:
73
74```typescript
75const index = yield* Cloudflare.VectorizeIndex("docs", {
76 dimensions: 768,
77 metric: "cosine",
78});
79
80yield* Cloudflare.VectorizeMetadataIndex("kind-index", {
81 indexName: index.indexName,
82 propertyName: "kind",
83 indexType: "string",
84});
85```
86
87With the metadata index in place (and created before vectors are
88inserted), `query` accepts a filter against that property:
89
90```typescript
91const matches = yield* docs.query([0.1, 0.2, 0.3], {
92 topK: 3,
93 filter: { kind: { $eq: "second" } },
94});
95```
96
97Metadata indexes are immutable too — changing the property name,
98type, or parent index replaces them.
99
100Thanks **[David J. Felix](https://github.com/DavidJFelix)** and
101**[John Royal](https://github.com/jdrydn)** for the
102contribution.
103([#407](https://github.com/alchemy-run/alchemy/pull/407))
104
105## Fix: state-store bootstrap no longer rotates your keys
106
107:::caution
108**Upgrade if you use the Cloudflare state store.** Prior versions
109re-generated the state store's encryption key and auth token on
110every bootstrap/update, because the bootstrap path always started
111from fresh local state. A rotated encryption key means previously
112persisted state can no longer be decrypted.
113:::
114
115The bug: updating the state store stack always used a fresh local
116state, so the auth token — and, worse, the **encryption key** —
117rotated on every run. beta.46 fixes the bootstrap to read from
118**remote state**, which already holds the auth token and
119encryption key, so neither is regenerated. You can run
120
121```sh
122bun alchemy cloudflare bootstrap --force
123```
124
125to force an update of everything against the real state without
126rotating the keys.
127
128The same change hardens a few adjacent failure modes:
129
130- **Decrypt failures degrade gracefully** — a failed decrypt now
131 returns `undefined` instead of throwing.
132- **Invalid tokens self-heal** — an invalid auth token is
133 detected and refreshed.
134- **Missing store recovers** — if the profile thinks the state
135 store exists but it's actually gone, it's recreated.
136
137([#477](https://github.com/alchemy-run/alchemy/pull/477))
138
139## Where to go next
140
141- [Vectorize on Cloudflare](https://developers.cloudflare.com/vectorize/)
142- [CHANGELOG](https://github.com/alchemy-run/alchemy/blob/main/CHANGELOG.md#v200-beta46)
143- [Compare v2.0.0-beta.45 → v2.0.0-beta.46](https://github.com/alchemy-run/alchemy/compare/v2.0.0-beta.45...v2.0.0-beta.46)