@totemsdk/manifest 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/sign.ts DELETED
@@ -1,63 +0,0 @@
1
- /**
2
- * signManifest — signs a manifest with a WOTS key.
3
- *
4
- * Steps:
5
- * 1. Canonicalise the manifest as deterministic JSON (sorted keys).
6
- * 2. SHA3-256 the UTF-8 bytes → 32-byte digest.
7
- * 3. Sign with wotsSign(seed, keyIndex, digest).
8
- * 4. Derive address + PKdigest via wotsKeypairFromSeed + wotsAddressFromKeypair.
9
- * 5. Return SignedManifest<T>.
10
- *
11
- * signerPublicKey in SignedManifest is the 32-byte WOTS PKdigest (hex, 64 chars).
12
- * This is the exact value expected by verifyManifest (via wotsVerifyDigest).
13
- *
14
- * The caller is responsible for reserving the WOTS key index before calling
15
- * this function. This package does NOT depend on @totemsdk/wots-lease.
16
- */
17
-
18
- import { sha3_256 } from '@noble/hashes/sha3.js';
19
- import {
20
- wotsSign,
21
- wotsKeypairFromSeed,
22
- wotsAddressFromKeypair,
23
- bytesToHex,
24
- } from '@totemsdk/core';
25
- import type { Manifest, SignedManifest } from './types.js';
26
-
27
- /** Produce a deterministic canonical JSON string with sorted keys (recursive). */
28
- function canonicalJson(value: unknown): string {
29
- if (value === null || typeof value !== 'object') {
30
- return JSON.stringify(value);
31
- }
32
- if (Array.isArray(value)) {
33
- return '[' + value.map(canonicalJson).join(',') + ']';
34
- }
35
- const obj = value as Record<string, unknown>;
36
- const keys = Object.keys(obj).sort();
37
- const pairs = keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(obj[k])}`);
38
- return '{' + pairs.join(',') + '}';
39
- }
40
-
41
- export function manifestDigest(manifest: Manifest): Uint8Array {
42
- const canonical = canonicalJson(manifest);
43
- return sha3_256(new TextEncoder().encode(canonical));
44
- }
45
-
46
- export async function signManifest<T extends Manifest>(
47
- manifest: T,
48
- seed: Uint8Array,
49
- keyIndex: number,
50
- ): Promise<SignedManifest<T>> {
51
- const digest = manifestDigest(manifest);
52
- const sigBytes = wotsSign(seed, keyIndex, digest);
53
- const kp = wotsKeypairFromSeed(seed, keyIndex);
54
- const address = wotsAddressFromKeypair(kp);
55
-
56
- return {
57
- manifest,
58
- authorAddress: address,
59
- signerPublicKey: bytesToHex(kp.pk),
60
- signedAt: Date.now(),
61
- signature: bytesToHex(sigBytes),
62
- };
63
- }
package/src/types.ts DELETED
@@ -1,140 +0,0 @@
1
- /**
2
- * @totemsdk/manifest — Manifest type definitions
3
- *
4
- * Four manifest categories for the MVP:
5
- * AppManifest — human-facing Pear app
6
- * CapabilityManifest — ephemeral AI/agent service
7
- * DAppManifest — KISSVM contract / covenant
8
- * EdgeServiceManifest — any persistent Totem Edge service
9
- *
10
- * No network, no blockchain, no Hyperswarm — pure schema.
11
- */
12
-
13
- export type AppPermission =
14
- | 'wallet:read-balance'
15
- | 'wallet:request-payment'
16
- | 'omnia:open-channel'
17
- | 'omnia:update-channel'
18
- | 'lookup:watch-address'
19
- | 'kissvm:evaluate'
20
- | 'qvac:call-agent';
21
-
22
- export interface AppManifest {
23
- type: 'app';
24
- appId: string;
25
- name: string;
26
- version: string;
27
- authorAddress: string;
28
- pearTopicKey: string;
29
- price: string;
30
- priceToken?: string;
31
- subscriptionInterval?: number;
32
- category: string[];
33
- permissions: AppPermission[];
34
- iconCid?: string;
35
- description: string;
36
- repoUrl?: string;
37
- minTotemVersion: string;
38
- }
39
-
40
- export interface CapabilityManifest {
41
- type: 'capability';
42
- capabilityId: string;
43
- capabilityName: string;
44
- agentAddress: string;
45
- agentIdentityKey: string;
46
- description: string;
47
- inputSchema: object;
48
- outputSchema: object;
49
- pricePerCall: string;
50
- priceToken?: string;
51
- paymentChannel?: 'omnia' | 'onchain';
52
- maxLatencyMs?: number;
53
- maxCallsPerMinute?: number;
54
- expiresAt: number;
55
- tags: string[];
56
- }
57
-
58
- export interface DAppAbiEntry {
59
- name: string;
60
- description: string;
61
- params: { name: string; type: string; description?: string }[];
62
- }
63
-
64
- export interface DAppManifest {
65
- type: 'dapp';
66
- dappId: string;
67
- name: string;
68
- version: string;
69
- authorAddress: string;
70
- contractHash: string;
71
- contractSource?: string;
72
- abi: DAppAbiEntry[];
73
- price: string;
74
- priceToken?: string;
75
- category: string[];
76
- description: string;
77
- auditReport?: string;
78
- }
79
-
80
- export type EdgeServiceType =
81
- | 'sensor'
82
- | 'robot'
83
- | 'mqtt-feed'
84
- | 'proof-index'
85
- | 'lookup-provider'
86
- | 'omnia-router'
87
- | 'calibration-authority'
88
- | 'verifier'
89
- | 'machine-service'
90
- | 'other';
91
-
92
- export interface EdgeServiceManifest {
93
- type: 'edge-service';
94
- serviceId: string;
95
- name: string;
96
- version: string;
97
- operatorAddress: string;
98
- serviceType: EdgeServiceType;
99
- description: string;
100
- endpoints?: Array<{
101
- type: 'https' | 'mqtt' | 'hyperswarm' | 'websocket' | 'other';
102
- uri: string;
103
- }>;
104
- capabilities: string[];
105
- price?: string;
106
- priceToken?: string;
107
- paymentMethods?: Array<'omnia' | 'onchain' | 'invoice' | 'free'>;
108
- tags: string[];
109
- expiresAt?: number;
110
- minTotemVersion?: string;
111
- }
112
-
113
- export type Manifest =
114
- | AppManifest
115
- | CapabilityManifest
116
- | DAppManifest
117
- | EdgeServiceManifest;
118
-
119
- /**
120
- * Wraps any manifest with a WOTS signature.
121
- *
122
- * `signerPublicKey` — hex of the full WOTS public key (required for
123
- * self-contained verification via verifyManifest).
124
- * `authorAddress` — the Minima address of the signer, derived at sign time
125
- * and stored for quick policy checks without re-deriving from the public key.
126
- */
127
- export interface SignedManifest<T extends Manifest = Manifest> {
128
- manifest: T;
129
- authorAddress: string;
130
- signerPublicKey: string;
131
- signedAt: number;
132
- signature: string;
133
- rootIdentityProof?: string;
134
- }
135
-
136
- export interface VerifyResult {
137
- valid: boolean;
138
- reason?: string;
139
- signerAddress: string;
140
- }
package/src/verify.ts DELETED
@@ -1,67 +0,0 @@
1
- /**
2
- * verifyManifest — verifies a SignedManifest without external state.
3
- *
4
- * Steps:
5
- * 1. Recompute the canonical manifest digest.
6
- * 2. Verify the WOTS signature against the stored 32-byte PKdigest
7
- * (signerPublicKey field) using wotsVerifyDigest.
8
- * 3. Confirm authorAddress matches the manifest's address field.
9
- *
10
- * wotsVerifyDigest is used (rather than wotsVerify) because signerPublicKey
11
- * stores the 32-byte WOTS PKdigest as returned by wotsKeypairFromSeed.kp.pk.
12
- * The full 1088-byte key is not stored in SignedManifest to keep it compact.
13
- */
14
-
15
- import { wotsVerifyDigest, hexToBytes } from '@totemsdk/core';
16
- import type { SignedManifest, VerifyResult } from './types.js';
17
- import { manifestDigest } from './sign.js';
18
-
19
- function manifestAddressField(manifest: SignedManifest['manifest']): string {
20
- switch (manifest.type) {
21
- case 'app': return manifest.authorAddress;
22
- case 'capability': return manifest.agentAddress;
23
- case 'dapp': return manifest.authorAddress;
24
- case 'edge-service': return manifest.operatorAddress;
25
- default: {
26
- const _exhaustive: never = manifest;
27
- throw new Error(`verifyManifest: unknown manifest type: ${JSON.stringify(_exhaustive)}`);
28
- }
29
- }
30
- }
31
-
32
- export function verifyManifest(signed: SignedManifest): VerifyResult {
33
- const { manifest, signature, signerPublicKey, authorAddress } = signed;
34
-
35
- let sigBytes: Uint8Array;
36
- let pkDigest: Uint8Array;
37
- try {
38
- sigBytes = hexToBytes(signature);
39
- pkDigest = hexToBytes(signerPublicKey);
40
- } catch (e) {
41
- return { valid: false, reason: `hex decode failed: ${String(e)}`, signerAddress: authorAddress };
42
- }
43
-
44
- const digest = manifestDigest(manifest);
45
-
46
- let sigValid: boolean;
47
- try {
48
- sigValid = wotsVerifyDigest(sigBytes, digest, pkDigest);
49
- } catch (e) {
50
- return { valid: false, reason: `WOTS verify threw: ${String(e)}`, signerAddress: authorAddress };
51
- }
52
-
53
- if (!sigValid) {
54
- return { valid: false, reason: 'WOTS signature invalid', signerAddress: authorAddress };
55
- }
56
-
57
- const expectedAddress = manifestAddressField(manifest);
58
- if (authorAddress !== expectedAddress) {
59
- return {
60
- valid: false,
61
- reason: `authorAddress mismatch: signed by '${authorAddress}' but manifest declares '${expectedAddress}'`,
62
- signerAddress: authorAddress,
63
- };
64
- }
65
-
66
- return { valid: true, signerAddress: authorAddress };
67
- }