@proveanything/smartlinks 1.16.5 → 1.16.7
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/dist/api/attestations.d.ts +53 -4
- package/dist/api/attestations.js +60 -3
- package/dist/api/proof.js +3 -0
- package/dist/docs/API_SUMMARY.md +74 -2
- package/dist/docs/attestations.md +28 -0
- package/dist/docs/proof-share-grants.md +78 -5
- package/dist/openapi.yaml +152 -0
- package/dist/types/attestations.d.ts +69 -0
- package/dist/types/proof.d.ts +22 -2
- package/docs/API_SUMMARY.md +74 -2
- package/docs/attestations.md +28 -0
- package/docs/proof-share-grants.md +78 -5
- package/openapi.yaml +152 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Attestation, CreateAttestationInput, ListAttestationsParams, ListAttestationsResponse, AttestationSummaryParams, AttestationSummaryResponse, PublicAttestationSummaryResponse, AttestationLatestParams, AttestationLatestResponse, PublicAttestationLatestResponse, AttestationVerifyParams, ChainVerifyResult, AttestationTreeSummaryParams, AttestationTreeSummaryResponse, PublicAttestationTreeSummaryResponse, AttestationTreeLatestParams, AttestationTreeLatestResponse, PublicAttestationTreeLatestResponse, PublicListAttestationsResponse } from "../types/attestations";
|
|
1
|
+
import type { Attestation, CreateAttestationInput, ListAttestationsParams, ListAttestationsResponse, AttestationSummaryParams, AttestationSummaryResponse, PublicAttestationSummaryResponse, AttestationLatestParams, AttestationLatestResponse, PublicAttestationLatestResponse, AttestationVerifyParams, ChainVerifyResult, AttestationTreeSummaryParams, AttestationTreeSummaryResponse, PublicAttestationTreeSummaryResponse, AttestationTreeLatestParams, AttestationTreeLatestResponse, PublicAttestationTreeLatestResponse, PublicListAttestationsResponse, OwnerAttestationInput, CreateOwnerAttestationResponse, ModerateAttestationInput, ModerateAttestationResponse } from "../types/attestations";
|
|
2
2
|
/**
|
|
3
3
|
* Postgres-backed Attestations API (v2).
|
|
4
4
|
*
|
|
@@ -10,9 +10,14 @@ import type { Attestation, CreateAttestationInput, ListAttestationsParams, ListA
|
|
|
10
10
|
*
|
|
11
11
|
* ### Admin vs Public
|
|
12
12
|
* - **Admin** endpoints (`/admin/collection/:id/attestations`) require a valid
|
|
13
|
-
* admin session or bearer token. All three data zones are returned
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* admin session or bearer token. All three data zones are returned, and the
|
|
14
|
+
* business can write any zone/visibility ({@link create}).
|
|
15
|
+
* - **Public** endpoints (`/public/collection/:id/attestations`) are read-only,
|
|
16
|
+
* EXCEPT {@link publicCreate} (the proof OWNER, or a `contribute`-grant holder,
|
|
17
|
+
* authors an attestation — value + ownerData, visibility public|owner, never the
|
|
18
|
+
* admin zone) and {@link moderate} (owner/admin approves or rejects a contributed
|
|
19
|
+
* record). Owner elevation is via `Authorization: Bearer <Firebase ID token>`;
|
|
20
|
+
* contributor access via a grant token set with `setGrantToken`.
|
|
16
21
|
*
|
|
17
22
|
* @see docs/attestations.md
|
|
18
23
|
*/
|
|
@@ -207,6 +212,50 @@ export declare namespace attestations {
|
|
|
207
212
|
* ```
|
|
208
213
|
*/
|
|
209
214
|
function publicList(collectionId: string, params: ListAttestationsParams): Promise<PublicListAttestationsResponse>;
|
|
215
|
+
/**
|
|
216
|
+
* Create a public attestation — the counterpart to the admin {@link create}.
|
|
217
|
+
* Authorised two ways, same call (the server decides from the request):
|
|
218
|
+
* 1. the proof OWNER (identity, via `Authorization: Bearer <Firebase ID token>`)
|
|
219
|
+
* adds an attestation to their own item; or
|
|
220
|
+
* 2. a holder of a `contribute`-scope grant adds one — call
|
|
221
|
+
* {@link setGrantToken} with the grant token first; for a public-link
|
|
222
|
+
* (anonymous) grant, pass `guestName` for attribution.
|
|
223
|
+
*
|
|
224
|
+
* Guardrails (server-enforced): `value` + `ownerData` only (`adminData` dropped),
|
|
225
|
+
* `visibility` clamped to `'public' | 'owner'`, `authorId`/`grantId` server-stamped.
|
|
226
|
+
* If the contribute grant was issued with `moderate: true`, the returned record
|
|
227
|
+
* has `moderationStatus: 'pending'` — held to the owner until {@link moderate}.
|
|
228
|
+
* The record joins the same tamper-evident hash chain.
|
|
229
|
+
* POST /public/collection/:collectionId/attestations
|
|
230
|
+
* ```ts
|
|
231
|
+
* // Owner:
|
|
232
|
+
* await attestations.publicCreate('coll_123', {
|
|
233
|
+
* subjectType: 'proof', subjectId: 'proof_1',
|
|
234
|
+
* attestationType: 'condition-report',
|
|
235
|
+
* value: { grade: 'excellent' }, visibility: 'public',
|
|
236
|
+
* })
|
|
237
|
+
* // Contributor on a shared link:
|
|
238
|
+
* setGrantToken(shareToken)
|
|
239
|
+
* await attestations.publicCreate('coll_123', {
|
|
240
|
+
* subjectType: 'proof', subjectId: 'proof_1',
|
|
241
|
+
* attestationType: 'photo', value: { url }, visibility: 'public',
|
|
242
|
+
* guestName: 'Sam',
|
|
243
|
+
* })
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
function publicCreate(collectionId: string, data: OwnerAttestationInput): Promise<CreateOwnerAttestationResponse>;
|
|
247
|
+
/**
|
|
248
|
+
* Moderate a contributed attestation (proof OWNER by identity, or collection
|
|
249
|
+
* admin). `'approve'` releases it to its declared visibility; `'reject'` keeps
|
|
250
|
+
* it author + admin only. Only the `moderationStatus` changes — the hashed fact
|
|
251
|
+
* and its chain are untouched. Find pending items with
|
|
252
|
+
* {@link publicList} + `moderationStatus: 'pending'`.
|
|
253
|
+
* POST /public/collection/:collectionId/attestations/:attestationId/moderate
|
|
254
|
+
* ```ts
|
|
255
|
+
* await attestations.moderate('coll_123', 'att_uuid', { decision: 'approve' })
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
function moderate(collectionId: string, attestationId: string, input: ModerateAttestationInput): Promise<ModerateAttestationResponse>;
|
|
210
259
|
/**
|
|
211
260
|
* Time-series summary (public).
|
|
212
261
|
*
|
package/dist/api/attestations.js
CHANGED
|
@@ -23,9 +23,14 @@ function buildAttestationQuery(params) {
|
|
|
23
23
|
*
|
|
24
24
|
* ### Admin vs Public
|
|
25
25
|
* - **Admin** endpoints (`/admin/collection/:id/attestations`) require a valid
|
|
26
|
-
* admin session or bearer token. All three data zones are returned
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* admin session or bearer token. All three data zones are returned, and the
|
|
27
|
+
* business can write any zone/visibility ({@link create}).
|
|
28
|
+
* - **Public** endpoints (`/public/collection/:id/attestations`) are read-only,
|
|
29
|
+
* EXCEPT {@link publicCreate} (the proof OWNER, or a `contribute`-grant holder,
|
|
30
|
+
* authors an attestation — value + ownerData, visibility public|owner, never the
|
|
31
|
+
* admin zone) and {@link moderate} (owner/admin approves or rejects a contributed
|
|
32
|
+
* record). Owner elevation is via `Authorization: Bearer <Firebase ID token>`;
|
|
33
|
+
* contributor access via a grant token set with `setGrantToken`.
|
|
29
34
|
*
|
|
30
35
|
* @see docs/attestations.md
|
|
31
36
|
*/
|
|
@@ -273,6 +278,58 @@ export var attestations;
|
|
|
273
278
|
return request(path);
|
|
274
279
|
}
|
|
275
280
|
attestations.publicList = publicList;
|
|
281
|
+
/**
|
|
282
|
+
* Create a public attestation — the counterpart to the admin {@link create}.
|
|
283
|
+
* Authorised two ways, same call (the server decides from the request):
|
|
284
|
+
* 1. the proof OWNER (identity, via `Authorization: Bearer <Firebase ID token>`)
|
|
285
|
+
* adds an attestation to their own item; or
|
|
286
|
+
* 2. a holder of a `contribute`-scope grant adds one — call
|
|
287
|
+
* {@link setGrantToken} with the grant token first; for a public-link
|
|
288
|
+
* (anonymous) grant, pass `guestName` for attribution.
|
|
289
|
+
*
|
|
290
|
+
* Guardrails (server-enforced): `value` + `ownerData` only (`adminData` dropped),
|
|
291
|
+
* `visibility` clamped to `'public' | 'owner'`, `authorId`/`grantId` server-stamped.
|
|
292
|
+
* If the contribute grant was issued with `moderate: true`, the returned record
|
|
293
|
+
* has `moderationStatus: 'pending'` — held to the owner until {@link moderate}.
|
|
294
|
+
* The record joins the same tamper-evident hash chain.
|
|
295
|
+
* POST /public/collection/:collectionId/attestations
|
|
296
|
+
* ```ts
|
|
297
|
+
* // Owner:
|
|
298
|
+
* await attestations.publicCreate('coll_123', {
|
|
299
|
+
* subjectType: 'proof', subjectId: 'proof_1',
|
|
300
|
+
* attestationType: 'condition-report',
|
|
301
|
+
* value: { grade: 'excellent' }, visibility: 'public',
|
|
302
|
+
* })
|
|
303
|
+
* // Contributor on a shared link:
|
|
304
|
+
* setGrantToken(shareToken)
|
|
305
|
+
* await attestations.publicCreate('coll_123', {
|
|
306
|
+
* subjectType: 'proof', subjectId: 'proof_1',
|
|
307
|
+
* attestationType: 'photo', value: { url }, visibility: 'public',
|
|
308
|
+
* guestName: 'Sam',
|
|
309
|
+
* })
|
|
310
|
+
* ```
|
|
311
|
+
*/
|
|
312
|
+
async function publicCreate(collectionId, data) {
|
|
313
|
+
const path = `/public/collection/${encodeURIComponent(collectionId)}/attestations`;
|
|
314
|
+
return post(path, data);
|
|
315
|
+
}
|
|
316
|
+
attestations.publicCreate = publicCreate;
|
|
317
|
+
/**
|
|
318
|
+
* Moderate a contributed attestation (proof OWNER by identity, or collection
|
|
319
|
+
* admin). `'approve'` releases it to its declared visibility; `'reject'` keeps
|
|
320
|
+
* it author + admin only. Only the `moderationStatus` changes — the hashed fact
|
|
321
|
+
* and its chain are untouched. Find pending items with
|
|
322
|
+
* {@link publicList} + `moderationStatus: 'pending'`.
|
|
323
|
+
* POST /public/collection/:collectionId/attestations/:attestationId/moderate
|
|
324
|
+
* ```ts
|
|
325
|
+
* await attestations.moderate('coll_123', 'att_uuid', { decision: 'approve' })
|
|
326
|
+
* ```
|
|
327
|
+
*/
|
|
328
|
+
async function moderate(collectionId, attestationId, input) {
|
|
329
|
+
const path = `/public/collection/${encodeURIComponent(collectionId)}/attestations/${encodeURIComponent(attestationId)}/moderate`;
|
|
330
|
+
return post(path, input);
|
|
331
|
+
}
|
|
332
|
+
attestations.moderate = moderate;
|
|
276
333
|
/**
|
|
277
334
|
* Time-series summary (public).
|
|
278
335
|
*
|
package/dist/api/proof.js
CHANGED
|
@@ -222,6 +222,9 @@ export var proof;
|
|
|
222
222
|
body.audience = options.audience;
|
|
223
223
|
if (options.expiresAt)
|
|
224
224
|
body.expiresAt = options.expiresAt instanceof Date ? options.expiresAt.toISOString() : options.expiresAt;
|
|
225
|
+
// Only applied server-side when scope includes 'contribute'.
|
|
226
|
+
if (options.moderate !== undefined)
|
|
227
|
+
body.moderate = options.moderate;
|
|
225
228
|
return post(grantBase(collectionId, productId, proofId), body);
|
|
226
229
|
}
|
|
227
230
|
proof.createGrant = createGrant;
|
package/dist/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 1.16.
|
|
3
|
+
Version: 1.16.7 | Generated: 2026-09-09T11:29:21.917Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -2745,6 +2745,15 @@ interface Attestation {
|
|
|
2745
2745
|
unit?: string
|
|
2746
2746
|
source?: string
|
|
2747
2747
|
authorId?: string
|
|
2748
|
+
* When authored under a `contribute` grant (rather than by identity), the id of
|
|
2749
|
+
* the granting token — provenance for a contributed record. `null`/absent for
|
|
2750
|
+
* owner/admin/identity writes.
|
|
2751
|
+
grantId?: string | null
|
|
2752
|
+
* Moderation gate, orthogonal to {@link visibility} and excluded from the hash
|
|
2753
|
+
* chain. `'approved'` (default) is live; `'pending'` is held for owner review
|
|
2754
|
+
* (visible only to its author and owner/admin audiences); `'rejected'` was
|
|
2755
|
+
* declined. Contributions under a `moderate` grant start `'pending'`.
|
|
2756
|
+
moderationStatus?: AttestationModerationStatus
|
|
2748
2757
|
metadata?: Record<string, any>
|
|
2749
2758
|
contentHash: string
|
|
2750
2759
|
prevHash?: string
|
|
@@ -2797,6 +2806,39 @@ interface CreateAttestationInput {
|
|
|
2797
2806
|
}
|
|
2798
2807
|
```
|
|
2799
2808
|
|
|
2809
|
+
**OwnerAttestationInput** (interface)
|
|
2810
|
+
```typescript
|
|
2811
|
+
interface OwnerAttestationInput {
|
|
2812
|
+
subjectType: AttestationSubjectType
|
|
2813
|
+
subjectId: string
|
|
2814
|
+
attestationType: string
|
|
2815
|
+
recordedAt?: string
|
|
2816
|
+
visibility?: 'public' | 'owner'
|
|
2817
|
+
value?: Record<string, any>
|
|
2818
|
+
ownerData?: Record<string, any>
|
|
2819
|
+
unit?: string
|
|
2820
|
+
source?: string
|
|
2821
|
+
metadata?: Record<string, any>
|
|
2822
|
+
* Attribution for an anonymous (public-link) contribute-grant write. Ignored
|
|
2823
|
+
* for owner writes and for named-grant writes (attributed to the signed-in uid).
|
|
2824
|
+
guestName?: string
|
|
2825
|
+
}
|
|
2826
|
+
```
|
|
2827
|
+
|
|
2828
|
+
**ModerateAttestationInput** (interface)
|
|
2829
|
+
```typescript
|
|
2830
|
+
interface ModerateAttestationInput {
|
|
2831
|
+
decision: 'approve' | 'reject'
|
|
2832
|
+
}
|
|
2833
|
+
```
|
|
2834
|
+
|
|
2835
|
+
**ModerateAttestationResponse** (interface)
|
|
2836
|
+
```typescript
|
|
2837
|
+
interface ModerateAttestationResponse {
|
|
2838
|
+
attestation: Attestation
|
|
2839
|
+
}
|
|
2840
|
+
```
|
|
2841
|
+
|
|
2800
2842
|
**ListAttestationsResponse** (interface)
|
|
2801
2843
|
```typescript
|
|
2802
2844
|
interface ListAttestationsResponse {
|
|
@@ -2804,6 +2846,13 @@ interface ListAttestationsResponse {
|
|
|
2804
2846
|
}
|
|
2805
2847
|
```
|
|
2806
2848
|
|
|
2849
|
+
**CreateOwnerAttestationResponse** (interface)
|
|
2850
|
+
```typescript
|
|
2851
|
+
interface CreateOwnerAttestationResponse {
|
|
2852
|
+
attestation: Attestation
|
|
2853
|
+
}
|
|
2854
|
+
```
|
|
2855
|
+
|
|
2807
2856
|
**PublicListAttestationsResponse** (interface)
|
|
2808
2857
|
```typescript
|
|
2809
2858
|
interface PublicListAttestationsResponse {
|
|
@@ -2882,6 +2931,10 @@ interface ListAttestationsParams {
|
|
|
2882
2931
|
subjectType: AttestationSubjectType
|
|
2883
2932
|
subjectId: string
|
|
2884
2933
|
attestationType?: string
|
|
2934
|
+
* Filter by moderation state. Primarily for the owner review queue
|
|
2935
|
+
* (`moderationStatus: 'pending'`). ANDs with the server's audience gate, so a
|
|
2936
|
+
* public caller can never use it to widen access.
|
|
2937
|
+
moderationStatus?: AttestationModerationStatus
|
|
2885
2938
|
recordedAfter?: string
|
|
2886
2939
|
recordedBefore?: string
|
|
2887
2940
|
limit?: number
|
|
@@ -2946,6 +2999,8 @@ interface AttestationTreeLatestParams {
|
|
|
2946
2999
|
|
|
2947
3000
|
**AttestationVisibility** = `'public' | 'owner' | 'admin'`
|
|
2948
3001
|
|
|
3002
|
+
**AttestationModerationStatus** = `'approved' | 'pending' | 'rejected'`
|
|
3003
|
+
|
|
2949
3004
|
**AttestationAudience** = `'public' | 'owner' | 'admin'`
|
|
2950
3005
|
|
|
2951
3006
|
**AttestationGroupBy** = `'hour' | 'day' | 'week' | 'month'`
|
|
@@ -7541,6 +7596,9 @@ interface ProofGrant {
|
|
|
7541
7596
|
proofId: string
|
|
7542
7597
|
productId?: string | null
|
|
7543
7598
|
scope: GrantScope[]
|
|
7599
|
+
* `contribute` grants only: when true, records/attestations added under this
|
|
7600
|
+
* grant land `pending` (owner-only) until the owner approves them.
|
|
7601
|
+
moderate?: boolean
|
|
7544
7602
|
audience: GrantAudience
|
|
7545
7603
|
createdBy: string
|
|
7546
7604
|
expiresAt?: string | null
|
|
@@ -7559,6 +7617,11 @@ interface CreateGrantOptions {
|
|
|
7559
7617
|
scope: GrantScope[]
|
|
7560
7618
|
audience?: GrantAudience
|
|
7561
7619
|
expiresAt?: Date | string
|
|
7620
|
+
* Only meaningful with the `contribute` scope: hold contributions made under
|
|
7621
|
+
* this grant for owner review (they start `pending` and are owner-only until
|
|
7622
|
+
* approved). Ignored for other scopes. Defaults to `false` (contributions live
|
|
7623
|
+
* on write).
|
|
7624
|
+
moderate?: boolean
|
|
7562
7625
|
}
|
|
7563
7626
|
```
|
|
7564
7627
|
|
|
@@ -7656,7 +7719,7 @@ interface CancelTransferOptions {
|
|
|
7656
7719
|
|
|
7657
7720
|
**ProofFieldDef** = `ScopedFieldDef & { scope?: ProofFieldScope }`
|
|
7658
7721
|
|
|
7659
|
-
**GrantScope** = `'read' | 'comment' | 'admin' | 'verify_owner'`
|
|
7722
|
+
**GrantScope** = `'read' | 'comment' | 'contribute' | 'admin' | 'verify_owner'`
|
|
7660
7723
|
|
|
7661
7724
|
**RedeemGrantResult** = ``
|
|
7662
7725
|
|
|
@@ -9063,6 +9126,15 @@ Tree latest snapshot — most-recent record per type across a container subtree
|
|
|
9063
9126
|
params: ListAttestationsParams) → `Promise<PublicListAttestationsResponse>`
|
|
9064
9127
|
List attestations for a subject (public). Records with `visibility='admin'` are always excluded. Records with `visibility='owner'` are included only when the caller provides a valid Firebase ID token that resolves to the subject owner. The `audience` field in the response indicates the tier that was served. ```typescript const { attestations: records, audience } = await attestations.publicList('coll_123', { subjectType: 'proof', subjectId: 'proof-uuid', }) ```
|
|
9065
9128
|
|
|
9129
|
+
**publicCreate**(collectionId: string,
|
|
9130
|
+
data: OwnerAttestationInput) → `Promise<CreateOwnerAttestationResponse>`
|
|
9131
|
+
Create a public attestation — the counterpart to the admin {@link create}. Authorised two ways, same call (the server decides from the request): 1. the proof OWNER (identity, via `Authorization: Bearer <Firebase ID token>`) adds an attestation to their own item; or 2. a holder of a `contribute`-scope grant adds one — call {@link setGrantToken} with the grant token first; for a public-link (anonymous) grant, pass `guestName` for attribution. Guardrails (server-enforced): `value` + `ownerData` only (`adminData` dropped), `visibility` clamped to `'public' | 'owner'`, `authorId`/`grantId` server-stamped. If the contribute grant was issued with `moderate: true`, the returned record has `moderationStatus: 'pending'` — held to the owner until {@link moderate}. The record joins the same tamper-evident hash chain. POST /public/collection/:collectionId/attestations ```ts // Owner: await attestations.publicCreate('coll_123', { subjectType: 'proof', subjectId: 'proof_1', attestationType: 'condition-report', value: { grade: 'excellent' }, visibility: 'public', }) // Contributor on a shared link: setGrantToken(shareToken) await attestations.publicCreate('coll_123', { subjectType: 'proof', subjectId: 'proof_1', attestationType: 'photo', value: { url }, visibility: 'public', guestName: 'Sam', }) ```
|
|
9132
|
+
|
|
9133
|
+
**moderate**(collectionId: string,
|
|
9134
|
+
attestationId: string,
|
|
9135
|
+
input: ModerateAttestationInput) → `Promise<ModerateAttestationResponse>`
|
|
9136
|
+
Moderate a contributed attestation (proof OWNER by identity, or collection admin). `'approve'` releases it to its declared visibility; `'reject'` keeps it author + admin only. Only the `moderationStatus` changes — the hashed fact and its chain are untouched. Find pending items with {@link publicList} + `moderationStatus: 'pending'`. POST /public/collection/:collectionId/attestations/:attestationId/moderate ```ts await attestations.moderate('coll_123', 'att_uuid', { decision: 'approve' }) ```
|
|
9137
|
+
|
|
9066
9138
|
**publicSummary**(collectionId: string,
|
|
9067
9139
|
params: AttestationSummaryParams) → `Promise<PublicAttestationSummaryResponse>`
|
|
9068
9140
|
Time-series summary (public). Always served at `audience='public'`. Same parameters as the admin version. `attestationType` are required
|
|
@@ -159,6 +159,34 @@ The middleware resolves the UID and checks ownership:
|
|
|
159
159
|
|
|
160
160
|
When ownership is confirmed the request is served at `audience='owner'`, which includes `ownerData`.
|
|
161
161
|
|
|
162
|
+
**Owner write** — the proof owner may also *author* an attestation on their own item via `POST /api/v1/public/collection/:collectionId/attestations` (`attestations.publicCreate`). It is the public counterpart to the admin write, with tight guardrails:
|
|
163
|
+
|
|
164
|
+
- the caller must be authenticated and **own the linked proof** (identity — a read grant is not enough); a subject with no linked proof is rejected;
|
|
165
|
+
- they may write `value` and `ownerData` only — **`adminData` is dropped** (the business zone);
|
|
166
|
+
- `visibility` is clamped to `'public' | 'owner'` (never `'admin'`), defaulting to `'owner'`;
|
|
167
|
+
- `authorId` is forced to the caller and `metadata.authorType = 'owner'` is stamped.
|
|
168
|
+
|
|
169
|
+
**Contribute write (grant)** — the same endpoint also accepts a holder of a
|
|
170
|
+
`contribute`-scope share grant (set the token with `setGrantToken` first), so a
|
|
171
|
+
third party can add an attestation without owning the proof. Named grant →
|
|
172
|
+
attributed to the signed-in uid; public-link grant → anonymous, with `guestName`
|
|
173
|
+
for attribution; `grantId` is stamped either way. If the grant was issued with
|
|
174
|
+
`moderate: true`, the record is created with `moderationStatus: 'pending'` and is
|
|
175
|
+
held to the owner until approved.
|
|
176
|
+
|
|
177
|
+
**Moderation** — `moderationStatus` (`approved` | `pending` | `rejected`) is a gate
|
|
178
|
+
**orthogonal to `visibility`** and excluded from the hash chain. A `pending` record
|
|
179
|
+
is returned only to its author and to owner/admin audiences — never to the public,
|
|
180
|
+
whatever its target visibility — and is excluded from public analytics. The owner
|
|
181
|
+
(identity) or a collection admin resolves it with `attestations.moderate(collectionId,
|
|
182
|
+
attestationId, { decision: 'approve' | 'reject' })`; approve releases it to its
|
|
183
|
+
declared visibility, reject keeps it author+admin-only. The review queue is
|
|
184
|
+
`attestations.publicList(..., { moderationStatus: 'pending' })`. Flipping the status
|
|
185
|
+
leaves the hashed fact and its chain untouched. See
|
|
186
|
+
[Proof Share Grants → Contribute access](proof-share-grants.md#contribute-access--let-someone-add-to-a-proof).
|
|
187
|
+
|
|
188
|
+
The record joins the same append-only, hash-chained log as business/system writes. Business writes (any zone/visibility) remain admin-only.
|
|
189
|
+
|
|
162
190
|
### Visibility vs audience
|
|
163
191
|
|
|
164
192
|
`visibility` is a property of an **individual record** set at write time. `audience` describes the **caller's tier** resolved at read time. The server applies:
|
|
@@ -22,11 +22,12 @@ re-checks the grant **server-side** against the database, so revocation is immed
|
|
|
22
22
|
|-------|--------------------|
|
|
23
23
|
| `read` | read owner-tier data on the proof (attestations, threads, records, cases) |
|
|
24
24
|
| `comment` | create threads/replies on the proof (guest comments) |
|
|
25
|
+
| `contribute` | add attestations / records to the proof for the life of the grant (temporary contribute access) — optionally held for owner review via `moderate` |
|
|
25
26
|
| `admin` | read owner-tier data (reserved for elevated share cases; never exposes the platform admin zone) |
|
|
26
27
|
| `verify_owner` | redeem a shareable ownership **assertion** (not the account) |
|
|
27
28
|
|
|
28
29
|
A grant can carry several scopes, e.g. `['read', 'comment']` for a shareable,
|
|
29
|
-
commentable album.
|
|
30
|
+
commentable album, or `['read', 'contribute']` to let someone add photos.
|
|
30
31
|
|
|
31
32
|
### Security & lifecycle
|
|
32
33
|
|
|
@@ -124,6 +125,65 @@ Other grant holders (and the owner) see these comments because a `read` grant re
|
|
|
124
125
|
|
|
125
126
|
---
|
|
126
127
|
|
|
128
|
+
## Contribute access — let someone add to a proof
|
|
129
|
+
|
|
130
|
+
A `contribute` scope grant is **temporary write access**: the bearer can add
|
|
131
|
+
attestations (a photo, a video reference, a check-in) and app records to the proof
|
|
132
|
+
for the life of the grant — no account or proof claim required. It's the successor
|
|
133
|
+
to ad-hoc "claim windows": properly time-boxed (`expiresAt`), revocable, and voided
|
|
134
|
+
on ownership transfer like every other grant.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// Owner issues a contribute grant that expires in 48h and holds contributions
|
|
138
|
+
// for review before they go public.
|
|
139
|
+
const grant = await proof.createGrant(collectionId, productId, proofId, {
|
|
140
|
+
scope: ['read', 'contribute'],
|
|
141
|
+
moderate: true, // contributions land 'pending'
|
|
142
|
+
expiresAt: new Date(Date.now() + 48 * 60 * 60 * 1000),
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
// Contributor (on the shared link) adds a photo attestation.
|
|
146
|
+
setGrantToken(shareToken)
|
|
147
|
+
await attestation.publicCreate(collectionId, {
|
|
148
|
+
subjectType: 'proof', subjectId: proofId,
|
|
149
|
+
attestationType: 'photo',
|
|
150
|
+
value: { url: 'https://…/photo.jpg' },
|
|
151
|
+
visibility: 'public',
|
|
152
|
+
guestName: 'Sam', // attribution for a public-link bearer
|
|
153
|
+
})
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Moderation — the two-step review
|
|
157
|
+
|
|
158
|
+
When the grant is issued with `moderate: true`, each contribution is created with
|
|
159
|
+
`moderationStatus: 'pending'` and is **held to the owner**: it is returned only to
|
|
160
|
+
its own author and to owner/admin audiences — never to the public, whatever its
|
|
161
|
+
target `visibility`. The owner reviews and approves (or rejects) it:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// Owner lists what's waiting (owner-tier read = identity or an owner session):
|
|
165
|
+
const { attestations } = await attestation.publicList(collectionId, {
|
|
166
|
+
subjectType: 'proof', subjectId: proofId,
|
|
167
|
+
moderationStatus: 'pending',
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
// Approve → the record goes live at its declared visibility; reject → author+admin only.
|
|
171
|
+
await attestation.moderate(collectionId, attestations[0].id, { decision: 'approve' })
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The moderation gate is enforced **server-side** — a front end may badge a pending
|
|
175
|
+
item, but it is the server that withholds it from other viewers. Only
|
|
176
|
+
`moderationStatus` changes on approve/reject; the hashed fact and its chain are
|
|
177
|
+
untouched. Contributions under a grant issued with `moderate: false` (the default)
|
|
178
|
+
go live immediately.
|
|
179
|
+
|
|
180
|
+
> **Media note.** A contributed photo/video is best modelled as an attestation
|
|
181
|
+
> (`attestationType: 'photo'`, `value: { url }`) so it rides this moderation model.
|
|
182
|
+
> The legacy per-proof asset upload path is unchanged and is not grant- or
|
|
183
|
+
> moderation-aware.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
127
187
|
## Proof of ownership — `verify_owner`
|
|
128
188
|
|
|
129
189
|
Ownership itself is **not** a grant — it is `proof.ownerId`, established via the
|
|
@@ -154,6 +214,7 @@ granted proof (and only that proof):
|
|
|
154
214
|
- **Attestations** — `attestation.publicList({ subjectType: 'proof', subjectId })`
|
|
155
215
|
- **Threads / Records / Cases** — `app.threads.list`, `app.records.*`, `app.cases.list`, and the single-item GETs, filtered to the granted proof
|
|
156
216
|
- **Thread creation / replies** — with a `comment` scope grant (see below)
|
|
217
|
+
- **Attestation / record creation** — with a `contribute` scope grant (see [Contribute access](#contribute-access--let-someone-add-to-a-proof))
|
|
157
218
|
|
|
158
219
|
The token never exposes the platform `admin` zone, and only reveals `owner`-visibility
|
|
159
220
|
rows for the granted `proofId`.
|
|
@@ -175,14 +236,24 @@ at `sites/{collectionId}/apps/{appId}` — a `grant` branch alongside
|
|
|
175
236
|
"requireScope": "comment",
|
|
176
237
|
"enforce": { "visibility": "owner", "status": "open" }
|
|
177
238
|
}
|
|
239
|
+
},
|
|
240
|
+
"records": {
|
|
241
|
+
"grant": {
|
|
242
|
+
"allow": true,
|
|
243
|
+
"requireScope": "contribute",
|
|
244
|
+
"enforce": { "visibility": "owner" } // held to the owner until promoted
|
|
245
|
+
}
|
|
178
246
|
}
|
|
179
247
|
}
|
|
180
248
|
}
|
|
181
249
|
```
|
|
182
250
|
|
|
183
|
-
This enables grant-scoped commenting **without** opening up
|
|
184
|
-
`enforce.visibility: "owner"` keeps
|
|
185
|
-
owner and other grant holders, not the wider
|
|
251
|
+
This enables grant-scoped commenting and contribution **without** opening up
|
|
252
|
+
anonymous creation. The `enforce.visibility: "owner"` keeps the created object
|
|
253
|
+
private to the proof (visible to the owner and other grant holders, not the wider
|
|
254
|
+
public). Attestation contribution is gated the same way, by the `contribute` scope;
|
|
255
|
+
whether those contributions are held for review is set per-grant with `moderate`,
|
|
256
|
+
not in app config.
|
|
186
257
|
|
|
187
258
|
---
|
|
188
259
|
|
|
@@ -200,12 +271,13 @@ namespace proof {
|
|
|
200
271
|
function setGrantToken(token: string | undefined): void
|
|
201
272
|
function getGrantToken(): string | undefined
|
|
202
273
|
|
|
203
|
-
type GrantScope = 'read' | 'comment' | 'admin' | 'verify_owner'
|
|
274
|
+
type GrantScope = 'read' | 'comment' | 'contribute' | 'admin' | 'verify_owner'
|
|
204
275
|
|
|
205
276
|
interface CreateGrantOptions {
|
|
206
277
|
scope: GrantScope[] // at least one
|
|
207
278
|
audience?: { kind: 'public_link' } | { kind: 'named'; email?: string; userId?: string }
|
|
208
279
|
expiresAt?: Date | string
|
|
280
|
+
moderate?: boolean // 'contribute' only — hold for review
|
|
209
281
|
}
|
|
210
282
|
|
|
211
283
|
interface RedeemGrantOptions { guestName?: string }
|
|
@@ -219,6 +291,7 @@ interface ProofGrant {
|
|
|
219
291
|
proofId: string
|
|
220
292
|
productId?: string | null
|
|
221
293
|
scope: GrantScope[]
|
|
294
|
+
moderate?: boolean // 'contribute' grants: contributions held for review when true
|
|
222
295
|
audience: { kind: 'public_link' | 'named'; email?: string; userId?: string }
|
|
223
296
|
createdBy: string
|
|
224
297
|
expiresAt?: string | null
|