Skip to content

Commit 6963036

Browse files
committed
Add TypeScript port (vectorpin npm package)
Third reference implementation alongside Python and Rust. Same protocol version 1, same canonicalization (sorted-key compact JSON, NFC-normalized UTF-8, little-endian f32/f64 vector bytes). Pins produced by any of the three implementations verify on the other two; enforced by `testvectors/v1.json` and `negative_v1.json`, which the new TS test suite consumes byte-for-byte alongside the existing Rust cross_lang test. Layout (typescript/): src/ attestation.ts Pin / PinHeader / canonicalJsonStringify / base64url helpers hash.ts canonicalVectorBytes, hashText, hashVector signer.ts Signer with named-options pin() verifier.ts Verifier, VerifyErrorCode (string-valued enum matching the Python wire form) index.ts public API test/ hash.test.ts unit tests for canonicalization and SHA-256 signer-verifier.test.ts honest verify, every failure mode, rotation, JSON round-trip cross-lang.test.ts loads testvectors/*.json and asserts byte-for-byte parity with Python/Rust Crypto via @noble/ed25519 + @noble/hashes — pure JavaScript, no native deps, works in Node 20+, Deno, Bun, and Cloudflare Workers. The package is ESM-only; built artifacts ship from dist/. All 28 TS tests pass (including the cross-language fixture suite that exercises the same private seed and timestamps Python and Rust use).
1 parent e5915f1 commit 6963036

14 files changed

Lines changed: 1761 additions & 1 deletion

README.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
[![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
66
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
77
[![Rust stable](https://img.shields.io/badge/rust-stable-orange.svg)](https://www.rust-lang.org/)
8+
[![Node 20+](https://img.shields.io/badge/node-20+-green.svg)](https://nodejs.org/)
89
[![Status: alpha](https://img.shields.io/badge/status-alpha-orange.svg)](#status)
910
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20058256.svg)](https://doi.org/10.5281/zenodo.20058256)
1011

@@ -87,7 +88,32 @@ let result = verifier.verify_full::<&[f32]>(
8788
assert!(result.is_ok());
8889
```
8990

90-
The Python and Rust implementations are byte-for-byte compatible. A pin produced by either side verifies on both, enforced by shared test vectors at [`testvectors/v1.json`](testvectors/) consumed in both test suites.
91+
### TypeScript / JavaScript
92+
93+
```bash
94+
npm install vectorpin
95+
```
96+
97+
```ts
98+
import { Signer, Verifier } from 'vectorpin';
99+
100+
const signer = Signer.generate('prod-2026-05');
101+
const embedding = new Float32Array(/* ... 3072 floats from your model ... */);
102+
const pin = signer.pin({
103+
source: 'The quick brown fox.',
104+
model: 'text-embedding-3-large',
105+
vector: embedding,
106+
});
107+
108+
const verifier = new Verifier({ [signer.keyId]: signer.publicKeyBytes() });
109+
const result = verifier.verify(pin, {
110+
source: 'The quick brown fox.',
111+
vector: embedding,
112+
});
113+
if (!result.ok) throw new Error(`integrity failure: ${result.error}`);
114+
```
115+
116+
The Python, Rust, and TypeScript implementations are byte-for-byte compatible. A pin produced by any of them verifies on the other two, enforced by shared test vectors at [`testvectors/v1.json`](testvectors/) consumed in all three test suites. The TS port is pure JavaScript via [`@noble/ed25519`](https://github.com/paulmillr/noble-ed25519) and [`@noble/hashes`](https://github.com/paulmillr/noble-hashes), so it also runs in Deno, Bun, and edge runtimes.
91117

92118
## What VectorPin guarantees
93119

typescript/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules/
2+
dist/
3+
*.log
4+
.npm
5+
.DS_Store

typescript/README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# vectorpin (TypeScript)
2+
3+
TypeScript / JavaScript reference implementation of VectorPin, byte-for-byte compatible with the Python and Rust ports.
4+
5+
```bash
6+
npm install vectorpin
7+
```
8+
9+
```ts
10+
import { Signer, Verifier } from 'vectorpin';
11+
12+
// At ingestion time
13+
const signer = Signer.generate('prod-2026-05');
14+
const embedding = new Float32Array(/* ... 3072 floats from your model ... */);
15+
const pin = signer.pin({
16+
source: 'The quick brown fox.',
17+
model: 'text-embedding-3-large',
18+
vector: embedding,
19+
});
20+
// Store JSON.stringify-able pin alongside the embedding in your vector DB metadata.
21+
import { pinToJSON } from 'vectorpin';
22+
const json = pinToJSON(pin);
23+
24+
// At read/audit time
25+
const verifier = new Verifier({ [signer.keyId]: signer.publicKeyBytes() });
26+
const result = verifier.verify(pin, {
27+
source: 'The quick brown fox.',
28+
vector: embedding,
29+
});
30+
if (!result.ok) {
31+
throw new Error(`integrity failure: ${result.error} - ${result.detail}`);
32+
}
33+
```
34+
35+
## Compatibility
36+
37+
The TypeScript port consumes the same `testvectors/v1.json` and `testvectors/negative_v1.json` fixtures the Python and Rust ports use in CI. A pin produced by any of the three implementations verifies on the other two; canonical bytes and signatures are identical byte-for-byte.
38+
39+
## Runtime support
40+
41+
- Node.js 20+ (uses `Buffer.from(s, 'base64url')` and `globalThis.crypto.getRandomValues`).
42+
- The crypto path is pure JavaScript via [`@noble/ed25519`](https://github.com/paulmillr/noble-ed25519) and [`@noble/hashes`](https://github.com/paulmillr/noble-hashes), so it also works in Deno, Bun, and Cloudflare Workers. Replace the `Buffer.from('base64url')` calls if you target a runtime without `Buffer`.
43+
44+
## Build & test
45+
46+
```bash
47+
npm install
48+
npm run typecheck # tsc --noEmit
49+
npm test # node:test via tsx
50+
npm run build # emit dist/
51+
```
52+
53+
## License
54+
55+
Apache 2.0. See `../LICENSE`.

0 commit comments

Comments
 (0)