Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,32 @@ jobs:
bun-version: 1.3.14
- run: bun install --frozen-lockfile
- run: bun run check
- name: Compile the TypeScript consumer example
run: bun run tsc --noEmit --strict --module ESNext --moduleResolution Bundler --target ES2022 --lib ES2022,DOM,DOM.Iterable --types node examples/verify.ts
- name: Build the browser ESM bundle
run: bun build ./src/index.ts --target=browser --format=esm --outfile="$RUNNER_TEMP/open-receipt.browser.js"
- run: npm pack --dry-run

node-consumer:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20, 22]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
package-manager-cache: false
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.14
- run: bun install --frozen-lockfile
- name: Pack and install as a Node consumer
run: |
npm pack --pack-destination "$RUNNER_TEMP"
mkdir "$RUNNER_TEMP/consumer"
cd "$RUNNER_TEMP/consumer"
npm init --yes
npm install "$RUNNER_TEMP"/receiptprotocol-open-receipt-*.tgz
node --input-type=module --eval 'import { canonicalize, verifyOpenReceiptTrust } from "@receiptprotocol/open-receipt"; if (canonicalize({b: 2, a: 1}) !== "{\"a\":1,\"b\":2}" || typeof verifyOpenReceiptTrust !== "function") process.exit(1)'
21 changes: 21 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,26 @@ jobs:
run: bun run build
- name: Inspect package tarball
run: npm pack --dry-run
- name: Check exact version on npm
id: registry
shell: bash
run: |
package_name=$(node -p 'require("./package.json").name')
package_version=$(node -p 'require("./package.json").version')
set +e
npm_output=$(npm view "${package_name}@${package_version}" name version dist.integrity --json 2>&1)
npm_status=$?
set -e
if [ "$npm_status" -eq 0 ]; then
PACKAGE_NAME="$package_name" PACKAGE_VERSION="$package_version" PUBLISHED_METADATA="$npm_output" node -e 'const value = JSON.parse(process.env.PUBLISHED_METADATA); if (value.name !== process.env.PACKAGE_NAME || value.version !== process.env.PACKAGE_VERSION || typeof value["dist.integrity"] !== "string") throw new Error("Published npm metadata does not match the release package")'
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "Verified ${package_name}@${package_version}; npm publication will be skipped."
elif grep -q "E404" <<<"$npm_output"; then
echo "exists=false" >> "$GITHUB_OUTPUT"
else
echo "$npm_output" >&2
exit "$npm_status"
fi
- name: Publish public package with Trusted Publishing
if: steps.registry.outputs.exists != 'true'
run: npm publish --access public --tag latest
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## 0.2.1 - 2026-08-02

- Accept both canonical `sha256:<digest>` parent references and immutable
historical bare SHA-256 parent references.

## 0.2.0 - 2026-07-30

- Add issuer trust resolution through embedded, pinned-metadata, and HTTPS
WebPKI modes.
- Add signed, versioned issuer metadata.
- Add purpose-bound key lifecycle and historical verification.
- Add issuance attestations and signed issuance-log checkpoints.
- Preserve v0.1 APIs, schemas, vectors, and verification behavior.

## 0.1.0 - 2026-07-21

- Publish Receipt Evidence Specification v0.1, explicit JSON Schemas, public
Expand Down
95 changes: 74 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,88 @@
# Open Receipt v0.1 verifier
# Open Receipt 0.2

`@receiptprotocol/open-receipt` canonicalizes and verifies Receipt Evidence
Specification v0.1 events locally. Verification does not call Receipt's API.
Open Receipt is an open, versioned specification for portable, verifiable
commercial evidence.

Open Receipt 0.2 adds issuer trust resolution, purpose-bound key lifecycle,
historical verification, and issuance attestations while remaining backward
compatible with v0.1.

Install the current verifier:

```sh
npm install @receiptprotocol/open-receipt@0.2.1
```

## Trust-aware verification

```ts
import { verifyOpenReceipt, verifyOpenReceiptBundle } from "@receiptprotocol/open-receipt";
import {
createHttpsWebPkiResolver,
verifyOpenReceiptTrust,
} from "@receiptprotocol/open-receipt";

const resolver = createHttpsWebPkiResolver({
allowedIssuerOrigins: ["https://receiptprotocol.com"],
});

const result = await verifyOpenReceiptTrust(receipt, {
trustMode: "https_webpki",
resolver,
});

const result = await verifyOpenReceipt(receipt, { issuerMetadata: cachedMetadata });
if (!result.valid) throw new Error(result.errors.join(", "));
if (!result.signature.valid || !result.issuer.trusted) {
throw new Error(result.errors.join(", "));
}
```

An embedded public JWK is sufficient to prove that the document was signed by
the corresponding private key. It is not, by itself, proof of the issuer's
identity. Pass cached issuer metadata obtained through a trusted path to pin the
issuer identity and set `issuer_identity_trusted` to `true`.
Cryptographic signature validity and issuer identity trust are reported
separately. The verifier supports three trust modes:

The event is canonicalized with RFC 8785 JSON Canonicalization Scheme rules and
signed as a detached compact JWS using Ed25519 (`alg: EdDSA`). Parent event
hashes are SHA-256 digests of complete signed parent envelopes.
- `embedded_only` verifies the signature against the embedded public key but
does not establish a trusted issuer identity.
- `pinned_metadata` verifies against an explicitly trusted signed issuer
metadata snapshot and hash, including offline use.
- `https_webpki` resolves the exact signed metadata version from an allowlisted
HTTPS issuer origin and validates its hash chain.

Open Receipt v0.1 is an early open specification, not an adopted industry
standard. `validated` means a bound validator ran and passed for that event; it
does not mean a permanent certification or universal guarantee.
## Issuer trust and key lifecycle

Open Receipt 0.2 issuer metadata is signed, versioned, immutable, and
hash-chained. Public keys are bound to one purpose, including
`open_receipt_evidence`, `issuer_metadata`, and `issuance_log`.

Lifecycle states distinguish preactive, active, retired, revoked, compromised,
and destroyed keys. Historical verification evaluates the key state at the
Receipt's issuance time. Retired public keys remain available for verification;
revocation and compromise are evaluated from their effective timestamps.

## Issuance attestations and checkpoints

An optional issuance attestation binds a Receipt digest, evidence signing key,
issuance time, and append-only sequence under a separate issuance-log key.
Signed checkpoints commit to the current log position and hash. Together they
provide stronger evidence against fraudulent backdating after an evidence-key
compromise without exposing private commercial payloads.

## v0.1 compatibility

The original `verifyOpenReceipt` and `verifyOpenReceiptBundle` APIs, v0.1
schemas, and deterministic vectors remain supported. Existing v0.1 Receipts
are not rewritten or re-signed. A v0.1 Receipt without an issuance attestation
remains cryptographically verifiable with lower trust assurance.

## Repository contents

- [`SPECIFICATION.md`](./SPECIFICATION.md): Receipt Evidence Specification v0.1.
- [`schemas`](./schemas): explicit event, bundle, and issuer JSON Schemas.
- [`test-vectors`](./test-vectors): public valid, invalid, and compatibility vectors.
- [`src`](./src): dependency-free TypeScript verifier source.
- [`examples`](./examples): offline verification example.
- [`SPECIFICATION.md`](./SPECIFICATION.md) — Open Receipt 0.2 specification and
v0.1 compatibility rules.
- [`schemas`](./schemas) — v0.1 and v0.2 JSON Schemas.
- [`test-vectors`](./test-vectors) — preserved v0.1 vectors and the 30-case
deterministic v0.2 trust catalog.
- [`src`](./src) — dependency-free TypeScript verifier source.
- [`examples`](./examples) — trust-aware verification example.

Run `bun install --frozen-lockfile` and `bun run check` to type-check, test, and
build the package.

Learn more at [receiptprotocol.com/open-receipt](https://receiptprotocol.com/open-receipt)
or view the package on [npm](https://www.npmjs.com/package/@receiptprotocol/open-receipt).
Loading