@tinycloud/share-sdk 1.2.0-beta.4 → 1.2.0-beta.5
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/{addressed-publish-BDaGyQA9.d.ts → addressed-publish-BnSBJ-MX.d.ts} +3 -1
- package/dist/{addressed-publish-DVvTlGog.d.cts → addressed-publish-DtcLNG2m.d.cts} +3 -1
- package/dist/artifact.cjs +88 -13
- package/dist/artifact.cjs.map +1 -1
- package/dist/artifact.d.cts +28 -8
- package/dist/artifact.d.ts +28 -8
- package/dist/artifact.js +84 -14
- package/dist/artifact.js.map +1 -1
- package/dist/index.cjs +128 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -4
- package/dist/index.d.ts +90 -4
- package/dist/index.js +123 -4
- package/dist/index.js.map +1 -1
- package/dist/share-index.cjs +21 -1
- package/dist/share-index.cjs.map +1 -1
- package/dist/share-index.js +21 -1
- package/dist/share-index.js.map +1 -1
- package/dist/share-root.cjs +23 -1
- package/dist/share-root.cjs.map +1 -1
- package/dist/share-root.d.cts +1 -1
- package/dist/share-root.d.ts +1 -1
- package/dist/share-root.js +23 -1
- package/dist/share-root.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ShareEnvelopeV3, ShareEnvelopeV2 } from '@tinycloud/share-envelope';
|
|
1
|
+
import { ShareEnvelopeV3, ShareEnvelopeV2, ArtifactPin, UnsignedShareEnvelopeV3 } from '@tinycloud/share-envelope';
|
|
2
2
|
export { CheckBearerDelegationOptions, DelegationCheckResult, canonicalize, checkBearerDelegation, fromBase64Url, requiredResourceUri, resourceUriCovers, toBase64Url } from '@tinycloud/share-envelope';
|
|
3
|
-
import { S as ShareAuthorizationMethod, a as ShareAuthorizedContent, b as ShareAuthorizationAdapter, T as TargetPublishOutcome, c as TargetPublishInput, d as ShareAuthorizationResult, e as ShareTarget } from './addressed-publish-
|
|
4
|
-
export { A as AddressedOwnerRootInput, f as AddressedOwnerRootReceipt, g as AddressedPolicyRegistrationInput, h as AddressedPolicyRegistrationReceipt, i as AddressedPublishAuthority, j as AddressedSharePublishOptions, k as AddressedShareRequest, P as PreparedAddressedShare, l as ShareAuthorizationDenied, m as ShareAuthorizationReady, n as ShareAuthorizationRequired, o as TargetPublishAdapter, p as addressedCredentialRequirement, q as authorizationMethodForTarget, r as authorizeShare, s as normalizeShareTarget, t as prepareAddressedShare, u as publishAddressedShare, v as publishTargetShare, w as targetAuthorizationMethod } from './addressed-publish-
|
|
3
|
+
import { S as ShareAuthorizationMethod, a as ShareAuthorizedContent, b as ShareAuthorizationAdapter, T as TargetPublishOutcome, c as TargetPublishInput, d as ShareAuthorizationResult, e as ShareTarget } from './addressed-publish-DtcLNG2m.cjs';
|
|
4
|
+
export { A as AddressedOwnerRootInput, f as AddressedOwnerRootReceipt, g as AddressedPolicyRegistrationInput, h as AddressedPolicyRegistrationReceipt, i as AddressedPublishAuthority, j as AddressedSharePublishOptions, k as AddressedShareRequest, P as PreparedAddressedShare, l as ShareAuthorizationDenied, m as ShareAuthorizationReady, n as ShareAuthorizationRequired, o as TargetPublishAdapter, p as addressedCredentialRequirement, q as authorizationMethodForTarget, r as authorizeShare, s as normalizeShareTarget, t as prepareAddressedShare, u as publishAddressedShare, v as publishTargetShare, w as targetAuthorizationMethod } from './addressed-publish-DtcLNG2m.cjs';
|
|
5
5
|
export { C as CreateOwnerDelegationParams, D as DEFAULT_SHARE_LIFETIME_MS, a as DelegatedShareKey, M as MAX_CONTENT_BYTES, O as OWNER_SHARE_REGISTRATION_DOMAIN, b as OwnerDelegationPermission, c as OwnerDelegationReceipt, d as OwnerShareAction, e as OwnerShareDecryption, f as OwnerShareMatcher, g as OwnerSharePolicyRegistration, h as OwnerSharePolicyRegistrationReceipt, i as OwnerSharePolicyV2, P as PublishedShare, j as PublishedShareDeliveryMaterial, k as PublishedShareMetadata, R as RegisterOwnerSharePolicyParams, S as SHARE_CONTENT_LIMIT, l as SHARE_PUBLISH_RESULT_VERSION, m as SharePublishError, n as SharePublishErrorCode, o as SharePublishOptions, p as SharePublishTarget, q as SignedDelegation, r as canonicalOwnerSharePolicy, s as computeOwnerShareRegistrationCid, t as createDelegatedShareKey, u as createPolicyEnforcementDelegation, v as redactPublishedShare, w as restoreDelegatedShareKey, x as validateOwnerSharePolicyRegistration, y as validateOwnerSharePolicyRegistrationBytes } from './owner-policy-BqzRlkb6.cjs';
|
|
6
6
|
import { S as SenderShareRecord, a as SenderShareRecordStorage } from './history-CUWDK0lE.cjs';
|
|
7
7
|
export { E as EncryptedSenderShareHistory, b as EncryptedShareHistoryStorage, M as MemoryEncryptedShareHistoryStorage, c as MemorySenderShareKeyStorage, d as MemorySenderShareRecordStorage, e as SenderShareKeyStorage, f as SenderShareRecordProofs, g as SenderShareRevocationScope, h as SenderShareStore, i as historyRecordForPublishedShare } from './history-CUWDK0lE.cjs';
|
|
@@ -67,6 +67,11 @@ interface ShareMetadata {
|
|
|
67
67
|
readonly filename?: string;
|
|
68
68
|
readonly mode?: string;
|
|
69
69
|
};
|
|
70
|
+
/** Set when the share is pinned to one artifact version; the owner's signature covers it (TC-1428). */
|
|
71
|
+
readonly artifact?: {
|
|
72
|
+
readonly id: string;
|
|
73
|
+
readonly version: number;
|
|
74
|
+
};
|
|
70
75
|
}
|
|
71
76
|
interface ShareInspection {
|
|
72
77
|
readonly metadata: ShareMetadata;
|
|
@@ -485,4 +490,85 @@ declare function parseNativeShareUrl(value: string): string;
|
|
|
485
490
|
/** Receive through the public SDK without creating another transport protocol. */
|
|
486
491
|
declare function openNativeShare(sharing: NativeSharingService, link: string): Promise<unknown>;
|
|
487
492
|
|
|
488
|
-
|
|
493
|
+
/**
|
|
494
|
+
* Artifact pin verification (TC-1428). Small on purpose: this is the one place that decides whether an addressed
|
|
495
|
+
* share that reads an artifact version may be trusted.
|
|
496
|
+
*
|
|
497
|
+
* What is signed. The owner signs the whole Policy/v3 envelope (Ed25519, `verifyEnvelopeV3SignatureOnly`). `artifactPin` is a
|
|
498
|
+
* field of that envelope, so the signature covers it: artifact id, version, the sha256 of the version's
|
|
499
|
+
* `manifest.json`, and the sha256 and size of the one stored file the share reads. The manifest stays an owner-side
|
|
500
|
+
* record; a recipient never needs to fetch it. Policy/v3 already binds the file's ciphertext digest
|
|
501
|
+
* (`contentSource.initialCiphertextDigestHex`); the pin binds that digest to an artifact identity and a version, so a
|
|
502
|
+
* share cannot be re-pointed at another version or another artifact's path without breaking the signature.
|
|
503
|
+
*
|
|
504
|
+
* Trust model (accepted, TC-1428 finding 3): the owner is the trusted signer. The node has no write-once ACL, so the
|
|
505
|
+
* owner (or whoever holds the owner's session) can overwrite `versions/<n>/<file>` afterwards. A recipient then
|
|
506
|
+
* detects the substitution through the pin digest and fails closed; it cannot tell a malicious owner from a node
|
|
507
|
+
* serving stale or altered bytes, and does not try to.
|
|
508
|
+
*
|
|
509
|
+
* Fails closed: any addressed share whose content source lies under `artifacts/` must carry a valid pin that matches
|
|
510
|
+
* that source. A pin on any other path is refused. `artifacts/<id>/current/...` has no pin form in v1 and is refused.
|
|
511
|
+
*/
|
|
512
|
+
|
|
513
|
+
type ArtifactPinErrorCode = "missing-pin" | "unsupported-artifact-path" | "wrong-signer" | "bad-signature" | "pin-source-mismatch" | "pinned-share-writable" | "expectation-mismatch" | "file-digest-mismatch" | "manifest-mismatch";
|
|
514
|
+
declare class ArtifactPinError extends Error {
|
|
515
|
+
readonly code: ArtifactPinErrorCode;
|
|
516
|
+
/** The code is the message: stable, short, and what callers and tests match on. */
|
|
517
|
+
constructor(code: ArtifactPinErrorCode);
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* What the caller already knows about the share it is opening, from outside the envelope. Optional: a v1 pinned share
|
|
521
|
+
* names its version in its own signed pin and nothing outside the link says which one to expect, so no production
|
|
522
|
+
* caller passes it. A caller that does learn a version out of band (a listing, a "newer version" link) uses it to
|
|
523
|
+
* refuse an older or different pin.
|
|
524
|
+
*/
|
|
525
|
+
interface ExpectedArtifact {
|
|
526
|
+
readonly artifactId?: string;
|
|
527
|
+
readonly version?: number;
|
|
528
|
+
}
|
|
529
|
+
interface VerifyArtifactShareOptions {
|
|
530
|
+
/** The did:key the caller trusts as the sender. `signature.signerDid` is self-asserted and proves nothing alone. */
|
|
531
|
+
readonly expectedOwnerDid: string;
|
|
532
|
+
readonly expected?: ExpectedArtifact;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* The fields of an envelope (or of a share about to be built) that the pin binds. Taking a view lets the publisher run
|
|
536
|
+
* the identical check before it registers anything, and the recipient run it again where it reads.
|
|
537
|
+
*/
|
|
538
|
+
interface ArtifactBindingView {
|
|
539
|
+
readonly artifactPin?: ArtifactPin;
|
|
540
|
+
readonly target: {
|
|
541
|
+
readonly spaceId: string;
|
|
542
|
+
};
|
|
543
|
+
readonly contentSource: {
|
|
544
|
+
readonly kvResource: string;
|
|
545
|
+
readonly selector: "exact" | "prefix";
|
|
546
|
+
readonly mode: "mutable" | "immutable";
|
|
547
|
+
readonly initialCiphertextDigestHex?: string | undefined;
|
|
548
|
+
};
|
|
549
|
+
readonly resource: {
|
|
550
|
+
readonly kind: "exact" | "prefix";
|
|
551
|
+
readonly path: string;
|
|
552
|
+
};
|
|
553
|
+
/** Share actions (`read` | `list` | `edit`). */
|
|
554
|
+
readonly actions: readonly string[];
|
|
555
|
+
/** KV abilities the signed policy grants on the content source. */
|
|
556
|
+
readonly policyKvActions: readonly string[];
|
|
557
|
+
}
|
|
558
|
+
declare function artifactBindingView(envelope: UnsignedShareEnvelopeV3 | ShareEnvelopeV3): ArtifactBindingView;
|
|
559
|
+
/**
|
|
560
|
+
* The pin and the content source must say the same thing, and a pinned share must be read-only. Synchronous and
|
|
561
|
+
* signature-free, so the publisher can run it before registering anything and the recipient can re-run it at the
|
|
562
|
+
* point of reading. Returns the pin for an artifact share and `undefined` for any other share.
|
|
563
|
+
*/
|
|
564
|
+
declare function assertArtifactBinding(view: ArtifactBindingView, expected?: ExpectedArtifact): ArtifactPin | undefined;
|
|
565
|
+
/**
|
|
566
|
+
* Check that the trusted owner signed this envelope (and so its pin), then the pin binding. Throws `ArtifactPinError`;
|
|
567
|
+
* never returns a partial answer. Only the owner's Ed25519 signature over the envelope is checked here, because that
|
|
568
|
+
* is what authenticates the pin. The rest of the envelope (policy signature, owner roots, enforcer binding) is
|
|
569
|
+
* `verifyEnvelopeV3`'s job, and `resolvePolicyShare` runs both before it trusts a link. (Folding the two into one
|
|
570
|
+
* call was measured: +911 gzip bytes in the canonical ESM bundle, for a saved ed25519 verify.)
|
|
571
|
+
*/
|
|
572
|
+
declare function verifyArtifactShare(envelope: ShareEnvelopeV3, options: VerifyArtifactShareOptions): ArtifactPin | undefined;
|
|
573
|
+
|
|
574
|
+
export { type ArtifactBindingView, ArtifactPinError, type ArtifactPinErrorCode, CredentialInvitationError, type CredentialInvitationErrorCode, type CredentialInvitationReceipt, type CredentialInvitationResult, DEFAULT_MAX_CONTENT_BLOB_BYTES, DEFAULT_MAX_SEALED_BLOB_BYTES, type ExpectedArtifact, MemoryShareCache, NATIVE_SHARE_FRAGMENT_PARAMETER, type NativeShareResult, type NativeSharingService, type ParsedAddressedEnvelope, SHARE_RESULT_VERSION, SHARE_V2_PROTOCOL, SenderShareRecord, SenderShareRecordStorage, ShareAuthorizationAdapter, ShareAuthorizationMethod, ShareAuthorizationResult, ShareAuthorizedContent, type ShareCache, type ShareCacheEntry, type ShareCacheKey, type ShareCacheMetadata, type ShareDeliveryAdapter, type ShareErrorCode, type ShareErrorInfo, type ShareFetchOptions, type ShareHistoryView, type ShareInspection, type ShareMetadata, type ShareNodeTrust, ShareNotifyError, type ShareNotifyInput, type ShareNotifyResult, type ShareNotifyState, type SharePolicyChallenge, type SharePolicyDelegationLink, type SharePolicyPublishAdapter, type SharePolicyRootRevocation, type SharePolicySession, type SharePresentationMaterial, type ShareReceiveAuthorizationRequired, ShareReceiveError, type ShareReceiveOutcome, type ShareReceiveResult, ShareRecipientClient, type ShareRecipientClientOptions, type ShareRevocationAdapter, type ShareRevocationResult, ShareTarget, TargetPublishInput, TargetPublishOutcome, type VerifyArtifactShareOptions, artifactBindingView, assertArtifactBinding, canonicalShareFilename, claimShare, createAddressedAuthorization, createNativeShare, createShareV2HolderBindingArtifact, deliverCredentialInvitation, hasUnsafeFilenameCodePoint, inspectShare, listShares, notifyShare, openNativeShare, parseAddressedEnvelope, parseNativeShareUrl, publishPolicyShare, receiveShare, recipientMatchesShareRecord, resumeShareAuthorization, revokeShare, shareDeliveryWindowExpiresAt, showShare, toShareErrorInfo, verifyArtifactShare };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ShareEnvelopeV3, ShareEnvelopeV2 } from '@tinycloud/share-envelope';
|
|
1
|
+
import { ShareEnvelopeV3, ShareEnvelopeV2, ArtifactPin, UnsignedShareEnvelopeV3 } from '@tinycloud/share-envelope';
|
|
2
2
|
export { CheckBearerDelegationOptions, DelegationCheckResult, canonicalize, checkBearerDelegation, fromBase64Url, requiredResourceUri, resourceUriCovers, toBase64Url } from '@tinycloud/share-envelope';
|
|
3
|
-
import { S as ShareAuthorizationMethod, a as ShareAuthorizedContent, b as ShareAuthorizationAdapter, T as TargetPublishOutcome, c as TargetPublishInput, d as ShareAuthorizationResult, e as ShareTarget } from './addressed-publish-
|
|
4
|
-
export { A as AddressedOwnerRootInput, f as AddressedOwnerRootReceipt, g as AddressedPolicyRegistrationInput, h as AddressedPolicyRegistrationReceipt, i as AddressedPublishAuthority, j as AddressedSharePublishOptions, k as AddressedShareRequest, P as PreparedAddressedShare, l as ShareAuthorizationDenied, m as ShareAuthorizationReady, n as ShareAuthorizationRequired, o as TargetPublishAdapter, p as addressedCredentialRequirement, q as authorizationMethodForTarget, r as authorizeShare, s as normalizeShareTarget, t as prepareAddressedShare, u as publishAddressedShare, v as publishTargetShare, w as targetAuthorizationMethod } from './addressed-publish-
|
|
3
|
+
import { S as ShareAuthorizationMethod, a as ShareAuthorizedContent, b as ShareAuthorizationAdapter, T as TargetPublishOutcome, c as TargetPublishInput, d as ShareAuthorizationResult, e as ShareTarget } from './addressed-publish-BnSBJ-MX.js';
|
|
4
|
+
export { A as AddressedOwnerRootInput, f as AddressedOwnerRootReceipt, g as AddressedPolicyRegistrationInput, h as AddressedPolicyRegistrationReceipt, i as AddressedPublishAuthority, j as AddressedSharePublishOptions, k as AddressedShareRequest, P as PreparedAddressedShare, l as ShareAuthorizationDenied, m as ShareAuthorizationReady, n as ShareAuthorizationRequired, o as TargetPublishAdapter, p as addressedCredentialRequirement, q as authorizationMethodForTarget, r as authorizeShare, s as normalizeShareTarget, t as prepareAddressedShare, u as publishAddressedShare, v as publishTargetShare, w as targetAuthorizationMethod } from './addressed-publish-BnSBJ-MX.js';
|
|
5
5
|
export { C as CreateOwnerDelegationParams, D as DEFAULT_SHARE_LIFETIME_MS, a as DelegatedShareKey, M as MAX_CONTENT_BYTES, O as OWNER_SHARE_REGISTRATION_DOMAIN, b as OwnerDelegationPermission, c as OwnerDelegationReceipt, d as OwnerShareAction, e as OwnerShareDecryption, f as OwnerShareMatcher, g as OwnerSharePolicyRegistration, h as OwnerSharePolicyRegistrationReceipt, i as OwnerSharePolicyV2, P as PublishedShare, j as PublishedShareDeliveryMaterial, k as PublishedShareMetadata, R as RegisterOwnerSharePolicyParams, S as SHARE_CONTENT_LIMIT, l as SHARE_PUBLISH_RESULT_VERSION, m as SharePublishError, n as SharePublishErrorCode, o as SharePublishOptions, p as SharePublishTarget, q as SignedDelegation, r as canonicalOwnerSharePolicy, s as computeOwnerShareRegistrationCid, t as createDelegatedShareKey, u as createPolicyEnforcementDelegation, v as redactPublishedShare, w as restoreDelegatedShareKey, x as validateOwnerSharePolicyRegistration, y as validateOwnerSharePolicyRegistrationBytes } from './owner-policy-BqzRlkb6.js';
|
|
6
6
|
import { S as SenderShareRecord, a as SenderShareRecordStorage } from './history-CyT6x8h4.js';
|
|
7
7
|
export { E as EncryptedSenderShareHistory, b as EncryptedShareHistoryStorage, M as MemoryEncryptedShareHistoryStorage, c as MemorySenderShareKeyStorage, d as MemorySenderShareRecordStorage, e as SenderShareKeyStorage, f as SenderShareRecordProofs, g as SenderShareRevocationScope, h as SenderShareStore, i as historyRecordForPublishedShare } from './history-CyT6x8h4.js';
|
|
@@ -67,6 +67,11 @@ interface ShareMetadata {
|
|
|
67
67
|
readonly filename?: string;
|
|
68
68
|
readonly mode?: string;
|
|
69
69
|
};
|
|
70
|
+
/** Set when the share is pinned to one artifact version; the owner's signature covers it (TC-1428). */
|
|
71
|
+
readonly artifact?: {
|
|
72
|
+
readonly id: string;
|
|
73
|
+
readonly version: number;
|
|
74
|
+
};
|
|
70
75
|
}
|
|
71
76
|
interface ShareInspection {
|
|
72
77
|
readonly metadata: ShareMetadata;
|
|
@@ -485,4 +490,85 @@ declare function parseNativeShareUrl(value: string): string;
|
|
|
485
490
|
/** Receive through the public SDK without creating another transport protocol. */
|
|
486
491
|
declare function openNativeShare(sharing: NativeSharingService, link: string): Promise<unknown>;
|
|
487
492
|
|
|
488
|
-
|
|
493
|
+
/**
|
|
494
|
+
* Artifact pin verification (TC-1428). Small on purpose: this is the one place that decides whether an addressed
|
|
495
|
+
* share that reads an artifact version may be trusted.
|
|
496
|
+
*
|
|
497
|
+
* What is signed. The owner signs the whole Policy/v3 envelope (Ed25519, `verifyEnvelopeV3SignatureOnly`). `artifactPin` is a
|
|
498
|
+
* field of that envelope, so the signature covers it: artifact id, version, the sha256 of the version's
|
|
499
|
+
* `manifest.json`, and the sha256 and size of the one stored file the share reads. The manifest stays an owner-side
|
|
500
|
+
* record; a recipient never needs to fetch it. Policy/v3 already binds the file's ciphertext digest
|
|
501
|
+
* (`contentSource.initialCiphertextDigestHex`); the pin binds that digest to an artifact identity and a version, so a
|
|
502
|
+
* share cannot be re-pointed at another version or another artifact's path without breaking the signature.
|
|
503
|
+
*
|
|
504
|
+
* Trust model (accepted, TC-1428 finding 3): the owner is the trusted signer. The node has no write-once ACL, so the
|
|
505
|
+
* owner (or whoever holds the owner's session) can overwrite `versions/<n>/<file>` afterwards. A recipient then
|
|
506
|
+
* detects the substitution through the pin digest and fails closed; it cannot tell a malicious owner from a node
|
|
507
|
+
* serving stale or altered bytes, and does not try to.
|
|
508
|
+
*
|
|
509
|
+
* Fails closed: any addressed share whose content source lies under `artifacts/` must carry a valid pin that matches
|
|
510
|
+
* that source. A pin on any other path is refused. `artifacts/<id>/current/...` has no pin form in v1 and is refused.
|
|
511
|
+
*/
|
|
512
|
+
|
|
513
|
+
type ArtifactPinErrorCode = "missing-pin" | "unsupported-artifact-path" | "wrong-signer" | "bad-signature" | "pin-source-mismatch" | "pinned-share-writable" | "expectation-mismatch" | "file-digest-mismatch" | "manifest-mismatch";
|
|
514
|
+
declare class ArtifactPinError extends Error {
|
|
515
|
+
readonly code: ArtifactPinErrorCode;
|
|
516
|
+
/** The code is the message: stable, short, and what callers and tests match on. */
|
|
517
|
+
constructor(code: ArtifactPinErrorCode);
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* What the caller already knows about the share it is opening, from outside the envelope. Optional: a v1 pinned share
|
|
521
|
+
* names its version in its own signed pin and nothing outside the link says which one to expect, so no production
|
|
522
|
+
* caller passes it. A caller that does learn a version out of band (a listing, a "newer version" link) uses it to
|
|
523
|
+
* refuse an older or different pin.
|
|
524
|
+
*/
|
|
525
|
+
interface ExpectedArtifact {
|
|
526
|
+
readonly artifactId?: string;
|
|
527
|
+
readonly version?: number;
|
|
528
|
+
}
|
|
529
|
+
interface VerifyArtifactShareOptions {
|
|
530
|
+
/** The did:key the caller trusts as the sender. `signature.signerDid` is self-asserted and proves nothing alone. */
|
|
531
|
+
readonly expectedOwnerDid: string;
|
|
532
|
+
readonly expected?: ExpectedArtifact;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* The fields of an envelope (or of a share about to be built) that the pin binds. Taking a view lets the publisher run
|
|
536
|
+
* the identical check before it registers anything, and the recipient run it again where it reads.
|
|
537
|
+
*/
|
|
538
|
+
interface ArtifactBindingView {
|
|
539
|
+
readonly artifactPin?: ArtifactPin;
|
|
540
|
+
readonly target: {
|
|
541
|
+
readonly spaceId: string;
|
|
542
|
+
};
|
|
543
|
+
readonly contentSource: {
|
|
544
|
+
readonly kvResource: string;
|
|
545
|
+
readonly selector: "exact" | "prefix";
|
|
546
|
+
readonly mode: "mutable" | "immutable";
|
|
547
|
+
readonly initialCiphertextDigestHex?: string | undefined;
|
|
548
|
+
};
|
|
549
|
+
readonly resource: {
|
|
550
|
+
readonly kind: "exact" | "prefix";
|
|
551
|
+
readonly path: string;
|
|
552
|
+
};
|
|
553
|
+
/** Share actions (`read` | `list` | `edit`). */
|
|
554
|
+
readonly actions: readonly string[];
|
|
555
|
+
/** KV abilities the signed policy grants on the content source. */
|
|
556
|
+
readonly policyKvActions: readonly string[];
|
|
557
|
+
}
|
|
558
|
+
declare function artifactBindingView(envelope: UnsignedShareEnvelopeV3 | ShareEnvelopeV3): ArtifactBindingView;
|
|
559
|
+
/**
|
|
560
|
+
* The pin and the content source must say the same thing, and a pinned share must be read-only. Synchronous and
|
|
561
|
+
* signature-free, so the publisher can run it before registering anything and the recipient can re-run it at the
|
|
562
|
+
* point of reading. Returns the pin for an artifact share and `undefined` for any other share.
|
|
563
|
+
*/
|
|
564
|
+
declare function assertArtifactBinding(view: ArtifactBindingView, expected?: ExpectedArtifact): ArtifactPin | undefined;
|
|
565
|
+
/**
|
|
566
|
+
* Check that the trusted owner signed this envelope (and so its pin), then the pin binding. Throws `ArtifactPinError`;
|
|
567
|
+
* never returns a partial answer. Only the owner's Ed25519 signature over the envelope is checked here, because that
|
|
568
|
+
* is what authenticates the pin. The rest of the envelope (policy signature, owner roots, enforcer binding) is
|
|
569
|
+
* `verifyEnvelopeV3`'s job, and `resolvePolicyShare` runs both before it trusts a link. (Folding the two into one
|
|
570
|
+
* call was measured: +911 gzip bytes in the canonical ESM bundle, for a saved ed25519 verify.)
|
|
571
|
+
*/
|
|
572
|
+
declare function verifyArtifactShare(envelope: ShareEnvelopeV3, options: VerifyArtifactShareOptions): ArtifactPin | undefined;
|
|
573
|
+
|
|
574
|
+
export { type ArtifactBindingView, ArtifactPinError, type ArtifactPinErrorCode, CredentialInvitationError, type CredentialInvitationErrorCode, type CredentialInvitationReceipt, type CredentialInvitationResult, DEFAULT_MAX_CONTENT_BLOB_BYTES, DEFAULT_MAX_SEALED_BLOB_BYTES, type ExpectedArtifact, MemoryShareCache, NATIVE_SHARE_FRAGMENT_PARAMETER, type NativeShareResult, type NativeSharingService, type ParsedAddressedEnvelope, SHARE_RESULT_VERSION, SHARE_V2_PROTOCOL, SenderShareRecord, SenderShareRecordStorage, ShareAuthorizationAdapter, ShareAuthorizationMethod, ShareAuthorizationResult, ShareAuthorizedContent, type ShareCache, type ShareCacheEntry, type ShareCacheKey, type ShareCacheMetadata, type ShareDeliveryAdapter, type ShareErrorCode, type ShareErrorInfo, type ShareFetchOptions, type ShareHistoryView, type ShareInspection, type ShareMetadata, type ShareNodeTrust, ShareNotifyError, type ShareNotifyInput, type ShareNotifyResult, type ShareNotifyState, type SharePolicyChallenge, type SharePolicyDelegationLink, type SharePolicyPublishAdapter, type SharePolicyRootRevocation, type SharePolicySession, type SharePresentationMaterial, type ShareReceiveAuthorizationRequired, ShareReceiveError, type ShareReceiveOutcome, type ShareReceiveResult, ShareRecipientClient, type ShareRecipientClientOptions, type ShareRevocationAdapter, type ShareRevocationResult, ShareTarget, TargetPublishInput, TargetPublishOutcome, type VerifyArtifactShareOptions, artifactBindingView, assertArtifactBinding, canonicalShareFilename, claimShare, createAddressedAuthorization, createNativeShare, createShareV2HolderBindingArtifact, deliverCredentialInvitation, hasUnsafeFilenameCodePoint, inspectShare, listShares, notifyShare, openNativeShare, parseAddressedEnvelope, parseNativeShareUrl, publishPolicyShare, receiveShare, recipientMatchesShareRecord, resumeShareAuthorization, revokeShare, shareDeliveryWindowExpiresAt, showShare, toShareErrorInfo, verifyArtifactShare };
|
package/dist/index.js
CHANGED
|
@@ -5341,6 +5341,20 @@ var v3TargetSchema = external_exports.object({
|
|
|
5341
5341
|
nodeAudience: external_exports.string().min(1),
|
|
5342
5342
|
spaceId: external_exports.string().refine(isCanonicalPathSegment, { message: "expected a canonical space id" })
|
|
5343
5343
|
}).strict();
|
|
5344
|
+
var ARTIFACT_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{2,62}$/;
|
|
5345
|
+
var artifactPinSchema = external_exports.object({
|
|
5346
|
+
v: external_exports.literal(1),
|
|
5347
|
+
artifactId: external_exports.string().regex(ARTIFACT_ID_PATTERN),
|
|
5348
|
+
version: external_exports.number().int().min(1).max(Number.MAX_SAFE_INTEGER),
|
|
5349
|
+
/** SHA-256 (lowercase hex) of the exact `manifest.json` bytes. The manifest itself stays an owner-side record. */
|
|
5350
|
+
manifestSha256: external_exports.string().regex(/^[0-9a-f]{64}$/),
|
|
5351
|
+
/** An addressed share reads one exact file, so exactly one entry: the stored bytes (ciphertext) it will fetch. */
|
|
5352
|
+
files: external_exports.array(external_exports.object({
|
|
5353
|
+
path: external_exports.string().min(1).max(255),
|
|
5354
|
+
sha256: external_exports.string().regex(/^[0-9a-f]{64}$/),
|
|
5355
|
+
bytes: external_exports.number().int().nonnegative()
|
|
5356
|
+
}).strict()).length(1)
|
|
5357
|
+
}).strict();
|
|
5344
5358
|
var unsignedShareEnvelopeV3BaseSchema = external_exports.object({
|
|
5345
5359
|
version: external_exports.literal(3),
|
|
5346
5360
|
shareId: external_exports.string().min(1),
|
|
@@ -5360,7 +5374,13 @@ var unsignedShareEnvelopeV3BaseSchema = external_exports.object({
|
|
|
5360
5374
|
expiry: external_exports.string().datetime({ offset: true }),
|
|
5361
5375
|
display: displaySchema,
|
|
5362
5376
|
encrypted: external_exports.literal(true),
|
|
5363
|
-
metadata: contentMetadataSchema
|
|
5377
|
+
metadata: contentMetadataSchema,
|
|
5378
|
+
/**
|
|
5379
|
+
* TC-1428: an owner-signed statement that this share reads one write-once artifact version. Additive: absent on
|
|
5380
|
+
* every other share, and covered by the envelope signature because the signature spans the whole unsigned object.
|
|
5381
|
+
* The shape is checked here; its binding to the content source is `verifyArtifactShare` in share-sdk.
|
|
5382
|
+
*/
|
|
5383
|
+
artifactPin: artifactPinSchema.optional()
|
|
5364
5384
|
}).strict();
|
|
5365
5385
|
function validateV3Invariants(value, ctx) {
|
|
5366
5386
|
const actions = [...value.actions];
|
|
@@ -5952,6 +5972,84 @@ async function parseSealedInlineShareUrl(url, options = {}) {
|
|
|
5952
5972
|
return { kind: "inline", ciphertextCid: record.cid, ciphertext, key32 };
|
|
5953
5973
|
}
|
|
5954
5974
|
|
|
5975
|
+
// src/artifact-core.ts
|
|
5976
|
+
var ARTIFACT_ROOT = "artifacts";
|
|
5977
|
+
function parseArtifactVersionPath(path) {
|
|
5978
|
+
const match = /^artifacts\/([a-z0-9][a-z0-9_-]{2,62})\/versions\/([1-9][0-9]{0,14})\/([^/]+)$/.exec(path);
|
|
5979
|
+
if (match === null) return void 0;
|
|
5980
|
+
return { artifactId: match[1], version: Number(match[2]), file: match[3] };
|
|
5981
|
+
}
|
|
5982
|
+
|
|
5983
|
+
// src/artifact-pin.ts
|
|
5984
|
+
var ArtifactPinError = class extends Error {
|
|
5985
|
+
code;
|
|
5986
|
+
/** The code is the message: stable, short, and what callers and tests match on. */
|
|
5987
|
+
constructor(code3) {
|
|
5988
|
+
super(code3);
|
|
5989
|
+
this.name = "ArtifactPinError";
|
|
5990
|
+
this.code = code3;
|
|
5991
|
+
}
|
|
5992
|
+
};
|
|
5993
|
+
function artifactBindingView(envelope) {
|
|
5994
|
+
return {
|
|
5995
|
+
...envelope.artifactPin === void 0 ? {} : { artifactPin: envelope.artifactPin },
|
|
5996
|
+
target: envelope.target,
|
|
5997
|
+
contentSource: envelope.contentSource,
|
|
5998
|
+
resource: envelope.resource,
|
|
5999
|
+
actions: envelope.actions,
|
|
6000
|
+
policyKvActions: envelope.policy.capabilityCeiling.flatMap((capability) => capability.kind === "kv" ? capability.actions : [])
|
|
6001
|
+
};
|
|
6002
|
+
}
|
|
6003
|
+
function sourcePath(view) {
|
|
6004
|
+
const resource = view.contentSource.kvResource;
|
|
6005
|
+
const marker = "/kv/";
|
|
6006
|
+
const at = resource.indexOf(marker);
|
|
6007
|
+
if (at <= 0 || resource.slice(0, at) !== view.target.spaceId) {
|
|
6008
|
+
throw new ArtifactPinError("pin-source-mismatch");
|
|
6009
|
+
}
|
|
6010
|
+
return resource.slice(at + marker.length);
|
|
6011
|
+
}
|
|
6012
|
+
function assertArtifactBinding(view, expected = {}) {
|
|
6013
|
+
const pin = view.artifactPin;
|
|
6014
|
+
const path = sourcePath(view);
|
|
6015
|
+
const inArtifacts = path === ARTIFACT_ROOT || path.startsWith(`${ARTIFACT_ROOT}/`);
|
|
6016
|
+
if (!inArtifacts) {
|
|
6017
|
+
if (pin !== void 0) throw new ArtifactPinError("pin-source-mismatch");
|
|
6018
|
+
return void 0;
|
|
6019
|
+
}
|
|
6020
|
+
if (pin === void 0) throw new ArtifactPinError("missing-pin");
|
|
6021
|
+
const parsed = parseArtifactVersionPath(path);
|
|
6022
|
+
if (parsed === void 0) throw new ArtifactPinError("unsupported-artifact-path");
|
|
6023
|
+
const file = pin.files[0];
|
|
6024
|
+
const source = view.contentSource;
|
|
6025
|
+
if (
|
|
6026
|
+
// The stored ciphertext of one file: bounded like content (100 MiB) plus the envelope's own framing.
|
|
6027
|
+
file.bytes > 128 * 1024 * 1024 || pin.artifactId !== parsed.artifactId || pin.version !== parsed.version || file.path !== parsed.file || view.resource.kind !== "exact" || view.resource.path !== path || source.selector !== "exact" || source.mode !== "immutable" || source.initialCiphertextDigestHex !== file.sha256
|
|
6028
|
+
) {
|
|
6029
|
+
throw new ArtifactPinError("pin-source-mismatch");
|
|
6030
|
+
}
|
|
6031
|
+
if (view.actions.includes("edit") || view.policyKvActions.includes("tinycloud.kv/put")) {
|
|
6032
|
+
throw new ArtifactPinError("pinned-share-writable");
|
|
6033
|
+
}
|
|
6034
|
+
if (expected.artifactId !== void 0 && expected.artifactId !== pin.artifactId || expected.version !== void 0 && expected.version !== pin.version) {
|
|
6035
|
+
throw new ArtifactPinError("expectation-mismatch");
|
|
6036
|
+
}
|
|
6037
|
+
return pin;
|
|
6038
|
+
}
|
|
6039
|
+
function verifyArtifactShare(envelope, options) {
|
|
6040
|
+
if (envelope.signature.signerDid !== options.expectedOwnerDid) {
|
|
6041
|
+
throw new ArtifactPinError("wrong-signer");
|
|
6042
|
+
}
|
|
6043
|
+
let valid;
|
|
6044
|
+
try {
|
|
6045
|
+
valid = verifyEnvelopeV3SignatureOnly(envelope) && envelope.policy.ownerDid === options.expectedOwnerDid;
|
|
6046
|
+
} catch {
|
|
6047
|
+
valid = false;
|
|
6048
|
+
}
|
|
6049
|
+
if (!valid) throw new ArtifactPinError("bad-signature");
|
|
6050
|
+
return assertArtifactBinding(artifactBindingView(envelope), options.expected);
|
|
6051
|
+
}
|
|
6052
|
+
|
|
5955
6053
|
// src/receive.ts
|
|
5956
6054
|
var SHARE_RESULT_VERSION = 1;
|
|
5957
6055
|
var DEFAULT_MAX_SEALED_BLOB_BYTES = 100 * 1024 * 1024 + 29;
|
|
@@ -5988,7 +6086,8 @@ function metadataFor(envelope, origin) {
|
|
|
5988
6086
|
...envelope.display.senderName === void 0 ? {} : { senderName: envelope.display.senderName },
|
|
5989
6087
|
...envelope.display.filename === void 0 ? {} : { filename: envelope.display.filename },
|
|
5990
6088
|
...envelope.display.mode === void 0 ? {} : { mode: envelope.display.mode }
|
|
5991
|
-
}
|
|
6089
|
+
},
|
|
6090
|
+
...envelope.artifactPin === void 0 ? {} : { artifact: { id: envelope.artifactPin.artifactId, version: envelope.artifactPin.version } }
|
|
5992
6091
|
};
|
|
5993
6092
|
}
|
|
5994
6093
|
async function resolvePolicyShare(link, options) {
|
|
@@ -6019,6 +6118,12 @@ async function resolvePolicyShare(link, options) {
|
|
|
6019
6118
|
} catch {
|
|
6020
6119
|
throw new ShareReceiveError("signature-invalid", "share signature is invalid");
|
|
6021
6120
|
}
|
|
6121
|
+
try {
|
|
6122
|
+
verifyArtifactShare(envelope, { expectedOwnerDid: envelope.policy.ownerDid });
|
|
6123
|
+
} catch (error) {
|
|
6124
|
+
if (!(error instanceof ArtifactPinError)) throw error;
|
|
6125
|
+
throw new ShareReceiveError("envelope-invalid", `share artifact pin is invalid (${error.code})`);
|
|
6126
|
+
}
|
|
6022
6127
|
options.signal?.throwIfAborted();
|
|
6023
6128
|
const expiry = Date.parse(envelope.expiry);
|
|
6024
6129
|
if (!Number.isFinite(expiry)) throw new ShareReceiveError("envelope-invalid", "share expiry is invalid");
|
|
@@ -6737,6 +6842,14 @@ function assertMailboxCommitment(commitment, target) {
|
|
|
6737
6842
|
async function publishAddressedShare(input) {
|
|
6738
6843
|
assertSafeInput(input);
|
|
6739
6844
|
const { target } = prepareAddressedShare(input);
|
|
6845
|
+
assertArtifactBinding({
|
|
6846
|
+
...input.artifactPin === void 0 ? {} : { artifactPin: input.artifactPin },
|
|
6847
|
+
target: { spaceId: input.spaceId },
|
|
6848
|
+
contentSource: input.contentSource,
|
|
6849
|
+
resource: input.resource,
|
|
6850
|
+
actions: input.actions,
|
|
6851
|
+
policyKvActions: input.policyActions
|
|
6852
|
+
});
|
|
6740
6853
|
const options = { ...input, filename: canonicalShareFilename(input.filename) };
|
|
6741
6854
|
if (target.kind !== "recipientDid") assertMailboxCommitment(options.credentialRequirement, target);
|
|
6742
6855
|
const expiry = rfc3339Seconds(options.expiresAt);
|
|
@@ -6803,7 +6916,8 @@ async function publishAddressedShare(input) {
|
|
|
6803
6916
|
filename: options.filename,
|
|
6804
6917
|
...options.mediaType.startsWith("text/") ? { encoding: "utf-8" } : {},
|
|
6805
6918
|
...options.artifact === void 0 ? {} : { artifact: options.artifact }
|
|
6806
|
-
}
|
|
6919
|
+
},
|
|
6920
|
+
...options.artifactPin === void 0 ? {} : { artifactPin: options.artifactPin }
|
|
6807
6921
|
};
|
|
6808
6922
|
unsignedShareEnvelopeV3Schema.parse(unsigned);
|
|
6809
6923
|
const envelopeSignature = await options.authority.sign(sha2563(textEncoder.encode(`${ENVELOPE_V3_DOMAIN}${canonicalize(unsigned)}`)));
|
|
@@ -8497,6 +8611,7 @@ var ShareRecipientClient = class {
|
|
|
8497
8611
|
const envelope = this.options.envelope;
|
|
8498
8612
|
const signer = this.nativeSigner ?? this.signer;
|
|
8499
8613
|
if (envelope.version !== 3 || this.session === void 0 || this.v3Authorization === void 0 || this.v3NodeAudience === void 0 || signer === void 0) throw new Error("v3 policy session signer is required");
|
|
8614
|
+
assertArtifactBinding(artifactBindingView(envelope));
|
|
8500
8615
|
if (!matchesInitialCiphertextDigest(bytes3, envelope.contentSource.initialCiphertextDigestHex)) throw new Error("v3 owner KV ciphertext digest mismatch");
|
|
8501
8616
|
const encrypted = parseV3InlineEncryptedEnvelope(bytes3, envelope);
|
|
8502
8617
|
const receiverPrivateKey = crypto.getRandomValues(new Uint8Array(32));
|
|
@@ -8685,6 +8800,7 @@ async function openNativeShare(sharing, link) {
|
|
|
8685
8800
|
return sharing.receive(parseNativeShareUrl(link), { autoSubdelegate: false, useSessionKey: false });
|
|
8686
8801
|
}
|
|
8687
8802
|
export {
|
|
8803
|
+
ArtifactPinError,
|
|
8688
8804
|
CredentialInvitationError,
|
|
8689
8805
|
DEFAULT_MAX_CONTENT_BLOB_BYTES,
|
|
8690
8806
|
DEFAULT_MAX_SEALED_BLOB_BYTES,
|
|
@@ -8707,6 +8823,8 @@ export {
|
|
|
8707
8823
|
ShareReceiveError,
|
|
8708
8824
|
ShareRecipientClient,
|
|
8709
8825
|
addressedCredentialRequirement,
|
|
8826
|
+
artifactBindingView,
|
|
8827
|
+
assertArtifactBinding,
|
|
8710
8828
|
authorizationMethodForTarget,
|
|
8711
8829
|
authorizeShare,
|
|
8712
8830
|
canonicalOwnerSharePolicy,
|
|
@@ -8749,6 +8867,7 @@ export {
|
|
|
8749
8867
|
toBase64Url,
|
|
8750
8868
|
toShareErrorInfo,
|
|
8751
8869
|
validateOwnerSharePolicyRegistration,
|
|
8752
|
-
validateOwnerSharePolicyRegistrationBytes
|
|
8870
|
+
validateOwnerSharePolicyRegistrationBytes,
|
|
8871
|
+
verifyArtifactShare
|
|
8753
8872
|
};
|
|
8754
8873
|
//# sourceMappingURL=index.js.map
|