Skip to content

Commit 19b74ef

Browse files
committed
docs(trustless-id): clarify client/verifier flow and add use-case doc 📝
- Add JSR badge to README - Link USECASE.md in README documentation section - Add USECASE.md with parties, flow, delivery channel, and secure vs leak cases - Clarify client decodes and verifier only verifies in README and USAGE - Clarify hashId is client-side; verifier does not need hashId - Use backticks for API terms (connectorId, hashId, requestId, decode, verify) in README and USAGE - Update session ID description (one-time or stable per user) in README
1 parent 3e52943 commit 19b74ef

3 files changed

Lines changed: 128 additions & 39 deletions

File tree

README.md

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ Anonymous ID: request, decode, verify time-bound. No server, no storage.
66

77
[![Node](https://img.shields.io/badge/node-%3E%3D20-339933?logo=node.js&logoColor=white)](https://nodejs.org) [![Deno](https://img.shields.io/badge/deno-compatible-ffcb00?logo=deno&logoColor=000000)](https://deno.com) [![Bun](https://img.shields.io/badge/bun-compatible-f9f1e1?logo=bun&logoColor=000000)](https://bun.sh) [![Browser](https://img.shields.io/badge/browser-compatible-4285F4?logo=googlechrome&logoColor=white)](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
88

9-
[![Module type: ESM](https://img.shields.io/badge/module%20type-esm-brightgreen)](https://github.com/NeaByteLab/Trustless-ID) [![npm version](https://img.shields.io/npm/v/@neabyte/trustless-id.svg)](https://www.npmjs.org/package/@neabyte/trustless-id) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9+
[![Module type: ESM](https://img.shields.io/badge/module%20type-esm-brightgreen)](https://github.com/NeaByteLab/Trustless-ID) [![npm version](https://img.shields.io/npm/v/@neabyte/trustless-id.svg)](https://www.npmjs.org/package/@neabyte/trustless-id) [![JSR](https://jsr.io/badges/@neabyte/trustless-id)](https://jsr.io/@neabyte/trustless-id) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
1010

1111
</div>
1212

1313
## Features
1414

15-
- **Trustless** — No server or DB; key from a shared identifier both sides agree on.
16-
- **Connector-bound** — Same identifier = same pair; others can’t decode or verify.
17-
- **Request, decode, verify**Encode → decode to code → verify with user input (number or digits).
15+
- **Trustless** — No server or DB; key from a shared identifier (`connectorId`) both sides agree on.
16+
- **Connector-bound** — Same `connectorId` = same pair; others can’t `decode` or `verify`.
17+
- **Request, decode, verify**`request``decode` to `codeId``verify` with user input (number or digits).
1818
- **Time-bound** — Payloads expire in 1–60 seconds (configurable).
19-
- **One-time session ID**Unique per session (timestamp + nonce).
19+
- **Session ID**`hashId` can be one-time per session (default) or stable per user for repeated identity proof.
2020

2121
## Installation
2222

@@ -37,7 +37,7 @@ deno add jsr:@neabyte/trustless-id
3737

3838
## Quick Start
3939

40-
Use a **connector ID** (e.g. service URL or app identifier) on both sides. Generate a one-time **hashId**, create an **instance**, build a **requestId**, then decode to a numeric code or verify with a user secret.
40+
Use a **connector ID** (`connectorId`) (e.g. service URL or app identifier) on both sides. Generate a one-time `hashId` via `generate(connectorId)`, create an instance via `create(connectorId)`, build a `requestId` via `request(hashId, expireTime?)`, then **client** decodes to a numeric code (`codeId`) to show the user; **verifier** checks the user-entered code with `verify(requestId, secret)`.
4141

4242
```typescript
4343
import trustless from '@neabyte/trustless-id'
@@ -54,29 +54,28 @@ const instance = trustless.create(connectorId)
5454
// Build payload; send requestId to verifier (QR / link / form)
5555
const requestId = instance.request(hashId, 10)
5656

57-
// Verifier decodes to get code
57+
// Client decodes to get code (show to user so they can type it at verifier)
5858
const codeId = instance.decode(hashId, requestId)
5959
if (codeId !== null) {
6060
console.log('Code:', codeId)
61-
// Check user secret
61+
// Verifier side: check user-entered secret
6262
console.log('Verify:', instance.verify(requestId, codeId))
6363
}
6464
```
6565

6666
## TOTP vs Trustless-ID
6767

68-
| Aspect | **TOTP** | **Trustless-ID** |
69-
| -------------------- | ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
70-
| **Secret** | A shared secret is generated and stored by both the server and the user (e.g. in an authenticator app). | No stored secret. Only a connector ID (e.g. service URL or app identifier) is agreed on; the key is derived from it when needed. |
71-
| **Code derivation** | Code is computed from the shared secret and the current time window (e.g. 30s) using HMAC-SHA1. | Code is derived from the session’s hashId and the encoded requestId using a FNV-1a–style mix; time is embedded in the request payload. |
72-
| **Server / backend** | A server (or backend) must store the secret per user and validate the code on each login. | No server or database. Both sides use the same connector ID to derive the same key and verify the code locally. |
73-
| **Typical use** | Two-factor authentication (2FA): prove that the user possesses the shared secret at login time. | One-time pairing or handshake: both sides agree on a connector; one creates a request, the other decodes and verifies the user-entered code within a short time window. |
68+
| Aspect | **TOTP** | **Trustless-ID** |
69+
| -------------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
70+
| **Secret** | A shared secret is generated and stored by both the server and the user (e.g. in an authenticator app). | No stored secret. Only a connector ID (`connectorId`) (e.g. service URL or app identifier) is agreed on; the key is derived from it when needed. |
71+
| **Code derivation** | Code is computed from the shared secret and the current time window (e.g. 30s) using HMAC-SHA1. | Code is derived from the session’s `hashId` and the encoded `requestId` using a FNV-1a–style mix; time is embedded in the request payload. |
72+
| **Server / backend** | A server (or backend) must store the secret per user and validate the code on each login. | No server or database. Client uses same `connectorId` to derive key, decode to code; verifier uses same `connectorId` to verify the user-entered code locally. |
73+
| **Typical use** | Two-factor authentication (2FA): prove that the user possesses the shared secret at login time. | One-time pairing or repeated identity: same connector; client uses `request``decode` → code, verifier uses `verify`. Use a new `hashId` per session (anonymous) or one stable `hashId` per user (repeated proof, no server storage). |
7474

7575
## Documentation
7676

77-
The full API, flow, and type reference are in **[USAGE.md](USAGE.md)**.
78-
79-
That document describes how to use the library end-to-end and lists every method and type.
77+
- **[USAGE.md](USAGE.md)** — Full API, flow, and type reference; how to use the library end-to-end and every method and type.
78+
- **[USECASE.md](USECASE.md)** — Use cases and scenarios to clarify flow and architecture (actors, channels, secure vs leak conditions).
8079

8180
## Build & Test
8281

USAGE.md

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Usage
22

3-
API, flow, options — request, decode, verify time-bound IDs.
3+
API, flow, options — `request`, `decode`, `verify` time-bound IDs.
44

55
## Table of Contents
66

@@ -16,7 +16,7 @@ API, flow, options — request, decode, verify time-bound IDs.
1616

1717
## Quick Start
1818

19-
**Flow:** `connectorId` → hashId → instance request → decode → verify.
19+
**Flow:** `connectorId``hashId` → instance (via `create`) → `request``decode``verify`.
2020

2121
```typescript
2222
import trustless from '@neabyte/trustless-id'
@@ -33,10 +33,10 @@ const instance = trustless.create(connectorId)
3333
// Request with 10s expiry window; send requestId to verifier (QR / link / form)
3434
const requestId = instance.request(hashId, 10)
3535

36-
// Decode to code, or null if invalid/expired
36+
// Client: decode to code and show to user
3737
const codeId = instance.decode(hashId, requestId)
3838
if (codeId !== null) {
39-
// Verify user secret
39+
// Verifier: check user-entered secret
4040
const ok = instance.verify(requestId, codeId)
4141
}
4242
```
@@ -45,25 +45,25 @@ if (codeId !== null) {
4545

4646
1. **Connector** — Both client and verifier use the same `connectorId` (e.g. service URL).
4747
2. **Hash** — Client calls `trustless.generate(connectorId)` once per session; returns a 197-char hex `hashId` (unique per call). This is the session identifier.
48-
3. **Instance** — Both sides call `trustless.create(connectorId)` to get an instance bound to that connector.
49-
4. **Request** — Client calls `instance.request(hashId, expireTime?)` to get `requestId`. Send `requestId` and `hashId` to the verifier (e.g. QR, link, form).
50-
5. **Decode**Verifier calls `instance.decode(hashId, requestId)` to get numeric `codeId`, or `null` if invalid/expired. Verifier must use the same `hashId` received from the client.
51-
6. **Verify** — User enters the code (or it’s shown). Verifier calls `instance.verify(requestId, secret)`; returns `true` when not expired and code matches.
48+
3. **Instance** — Both sides call `trustless.create(connectorId)` to get an instance (for `request`, `decode`, `verify`) bound to that connector.
49+
4. **Request** — Client calls `instance.request(hashId, expireTime?)` to get `requestId`. Send `requestId` to the verifier (e.g. QR, link, form).
50+
5. **Decode****Client** calls `instance.decode(hashId, requestId)` to get numeric `codeId`, or `null` if invalid/expired. Client shows the code to the user so they can type it at the verifier. The verifier does not call `decode`.
51+
6. **Verify** — User enters the code at the verifier. **Verifier** calls `instance.verify(requestId, secret)` only; returns `true` when not expired and code matches.
5252

5353
## Methods Overview
5454

55-
| Method | Type | Returns | Description |
56-
| :--------------------------------------------------------------------------- | :------- | :--------------- | :------------------------------------------------ |
57-
| [`trustless.create(connectorId)`](#trustlesscreateconnectorid) | static | instance | Factory: new instance bound to connector. |
58-
| [`trustless.generate(connectorId)`](#trustlessgenerateconnectorid) | static | `HashId` | One-time 197-char hex hash. |
59-
| [`instance.request(hashId, expireTime?)`](#instancerequesthashid-expiretime) | instance | `RequestId` | Encoded payload string or `''` if hashId invalid. |
60-
| [`instance.decode(hashId, requestId)`](#instancedecodehashid-requestid) | instance | `CodeId \| null` | Numeric code when valid and not expired. |
61-
| [`instance.verify(requestId, secret)`](#instanceverifyrequestid-secret) | instance | `boolean` | True when not expired and secret matches code. |
55+
| Method | Type | Returns | Description |
56+
| :--------------------------------------------------------------------------- | :------- | :--------------- | :---------------------------------------------------- |
57+
| [`trustless.create(connectorId)`](#trustlesscreateconnectorid) | static | instance | Factory: new instance bound to connector. |
58+
| [`trustless.generate(connectorId)`](#trustlessgenerateconnectorid) | static | `HashId` | One-time 197-char hex hash. |
59+
| [`instance.request(hashId, expireTime?)`](#instancerequesthashid-expiretime) | instance | `RequestId` | Encoded payload string or `''` if `hashId` invalid. |
60+
| [`instance.decode(hashId, requestId)`](#instancedecodehashid-requestid) | instance | `CodeId \| null` | **Client:** numeric code when valid (show to user). |
61+
| [`instance.verify(requestId, secret)`](#instanceverifyrequestid-secret) | instance | `boolean` | **Verifier:** true when not expired and code matches. |
6262

6363
## ConnectorId and HashId
6464

6565
- **ConnectorId** — Any non-secret string that identifies the connector (e.g. `trustless://auth/example.com:0.1.0?service=none`). Trimmed before hashing. Same value on both sides yields the same encryption key.
66-
- **HashId** — 197 lowercase hex chars from `trustless.generate(connectorId)`. Includes timestamp and random nonce; different on every call. Client must pass `hashId` together with `requestId` to the verifier so the verifier can call `decode(hashId, requestId)`.
66+
- **HashId** — 197 lowercase hex chars from `trustless.generate(connectorId)`. Includes timestamp and random nonce; different on every call. Used by the **client** when calling `decode(hashId, requestId)` to obtain the code to show the user. The verifier does not need `hashId`; it only receives `requestId` and the user-entered code and calls `verify(requestId, secret)`.
6767

6868
```typescript
6969
const hashId = trustless.generate(connectorId)
@@ -105,7 +105,7 @@ Create an instance bound to the given connector. The instance key is a hash of t
105105

106106
### trustless.generate(connectorId)
107107

108-
Generate a one-time 197-char hex hash. Uses connectorId + timestamp + random nonce. Call once per session; each call returns a different value.
108+
Generate a one-time 197-char hex hash. Uses `connectorId` + timestamp + random nonce. Call once per session; each call returns a different value.
109109

110110
- `connectorId` `<ConnectorId>`: Same identifier as used for `create`.
111111
- Returns: `<HashId>` 197 lowercase hex characters.
@@ -120,18 +120,18 @@ Build the encoded request payload for the given hash and optional expiry window.
120120

121121
### instance.decode(hashId, requestId)
122122

123-
Decode request payload and return the numeric code when hash matches and not expired.
123+
**Client only.** Decode request payload and return the numeric code when hash matches and not expired. Use the returned code to show the user so they can enter it at the verifier.
124124

125125
- `hashId` `<HashId>`: Expected hash for this session.
126-
- `requestId` `<RequestId>`: Encoded payload from the client.
126+
- `requestId` `<RequestId>`: Encoded payload from `request`.
127127
- Returns: `<CodeId | null>` Integer in 0–1e10, or null when invalid/expired/wrong hash.
128128

129129
### instance.verify(requestId, secret)
130130

131-
Check that the user-provided secret matches the derived code and the payload is not expired.
131+
**Verifier only.** Check that the user-provided secret matches the derived code and the payload is not expired.
132132

133-
- `requestId` `<RequestId>`: Encoded payload.
134-
- `secret` `<VerifySecret>`: Number or string of digits (e.g. user input).
133+
- `requestId` `<RequestId>`: Encoded payload (received from client, e.g. via QR).
134+
- `secret` `<VerifySecret>`: Number or string of digits (user-entered code).
135135
- Returns: `<boolean>` True when not expired and code matches.
136136

137137
## Option Types

0 commit comments

Comments
 (0)