@ternent/core 0.0.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/.changeset/README.md +8 -0
- package/.changeset/config.json +17 -0
- package/.github/workflows/deploy-armour.yml +42 -0
- package/.github/workflows/deploy-identity.yml +42 -0
- package/.github/workflows/deploy-seal.yml +42 -0
- package/.github/workflows/deploy-ui.yml +42 -0
- package/.github/workflows/deploy-utils.yml +42 -0
- package/.github/workflows/release-create.yml +59 -0
- package/.github/workflows/release-publish.yml +54 -0
- package/.nvmrc +1 -0
- package/.ops/publish.mjs +31 -0
- package/package.json +16 -0
- package/packages/README.md +0 -0
- package/packages/armour/CHANGELOG.md +66 -0
- package/packages/armour/CLAUDE.md +8 -0
- package/packages/armour/README.md +103 -0
- package/packages/armour/SPEC.md +92 -0
- package/packages/armour/package.json +45 -0
- package/packages/armour/src/constants.ts +5 -0
- package/packages/armour/src/deps.d.ts +56 -0
- package/packages/armour/src/errors.ts +172 -0
- package/packages/armour/src/files.ts +73 -0
- package/packages/armour/src/identity.ts +72 -0
- package/packages/armour/src/index.ts +56 -0
- package/packages/armour/src/init.ts +10 -0
- package/packages/armour/src/passphrase.ts +33 -0
- package/packages/armour/src/recipients.ts +73 -0
- package/packages/armour/src/text.ts +68 -0
- package/packages/armour/src/types.ts +93 -0
- package/packages/armour/test/armour.test.ts +270 -0
- package/packages/armour/tsconfig.build.json +12 -0
- package/packages/armour/tsconfig.json +12 -0
- package/packages/armour/vite.config.ts +29 -0
- package/packages/concord/CHANGELOG.md +83 -0
- package/packages/concord/CLAUDE.md +9 -0
- package/packages/concord/README.md +146 -0
- package/packages/concord/SPEC.md +287 -0
- package/packages/concord/package.json +51 -0
- package/packages/concord/src/app.ts +717 -0
- package/packages/concord/src/errors.ts +9 -0
- package/packages/concord/src/index.ts +20 -0
- package/packages/concord/src/types.ts +127 -0
- package/packages/concord/test/concord.test.ts +978 -0
- package/packages/concord/tsconfig.json +12 -0
- package/packages/concord/vite.browser.config.ts +27 -0
- package/packages/concord/vite.config.ts +35 -0
- package/packages/concord/vite.config.ts.timestamp-1774262297922-ffd76e35ea668.mjs +83 -0
- package/packages/identity/CHANGELOG.md +47 -0
- package/packages/identity/README.md +236 -0
- package/packages/identity/package.json +41 -0
- package/packages/identity/src/index.ts +538 -0
- package/packages/identity/test/identity.test.ts +172 -0
- package/packages/identity/tsconfig.build.json +12 -0
- package/packages/identity/vite.config.ts +17 -0
- package/packages/ledger/CHANGELOG.md +69 -0
- package/packages/ledger/CLAUDE.md +9 -0
- package/packages/ledger/SPEC.md +304 -0
- package/packages/ledger/package.json +48 -0
- package/packages/ledger/src/index.ts +2 -0
- package/packages/ledger/src/ledger.ts +1286 -0
- package/packages/ledger/src/seal-cli.d.ts +25 -0
- package/packages/ledger/src/types.ts +294 -0
- package/packages/ledger/test/ledger.test.ts +838 -0
- package/packages/ledger/tsconfig.json +12 -0
- package/packages/ledger/vite.browser.config.ts +27 -0
- package/packages/ledger/vite.config.ts +39 -0
- package/packages/seal/CHANGELOG.md +137 -0
- package/packages/seal/CLAUDE.md +8 -0
- package/packages/seal/README.md +258 -0
- package/packages/seal/bin/seal +6 -0
- package/packages/seal/package.json +59 -0
- package/packages/seal/src/artifact.ts +380 -0
- package/packages/seal/src/cli.ts +372 -0
- package/packages/seal/src/commands/identity.ts +52 -0
- package/packages/seal/src/commands/manifest.ts +71 -0
- package/packages/seal/src/commands/publicKey.ts +7 -0
- package/packages/seal/src/commands/sign.ts +56 -0
- package/packages/seal/src/commands/verify.ts +54 -0
- package/packages/seal/src/crypto.ts +85 -0
- package/packages/seal/src/errors.ts +88 -0
- package/packages/seal/src/index.ts +5 -0
- package/packages/seal/src/manifest.ts +114 -0
- package/packages/seal/src/node.ts +18 -0
- package/packages/seal/src/proof.ts +344 -0
- package/packages/seal/test/artifact.test.ts +86 -0
- package/packages/seal/test/cli.test.ts +208 -0
- package/packages/seal/test/crypto.test.ts +21 -0
- package/packages/seal/test/manifest.test.ts +32 -0
- package/packages/seal/test/proof.test.ts +60 -0
- package/packages/seal/tsconfig.json +12 -0
- package/packages/seal/vite.config.ts +54 -0
- package/packages/ui/CHANGELOG.md +393 -0
- package/packages/ui/README.md +57 -0
- package/packages/ui/jsconfig.json +19 -0
- package/packages/ui/package.json +64 -0
- package/packages/ui/scripts/check-tokens.js +56 -0
- package/packages/ui/scripts/generate-theme-css.mjs +85 -0
- package/packages/ui/src/design-system/base.css +8 -0
- package/packages/ui/src/design-system/docs/ACCESSIBILITY_RULES.md +186 -0
- package/packages/ui/src/design-system/docs/AI_SYSTEM.md +281 -0
- package/packages/ui/src/design-system/docs/PATTERN_RULES.md +83 -0
- package/packages/ui/src/design-system/docs/PRIMITIVE_RULES.md +258 -0
- package/packages/ui/src/design-system/docs/TOKEN_RULES.md +235 -0
- package/packages/ui/src/design-system/docs/VISUAL_DIRECTION.md +68 -0
- package/packages/ui/src/design-system/foundation.js +420 -0
- package/packages/ui/src/design-system/tokens.css +140 -0
- package/packages/ui/src/design-system/tokens.js +327 -0
- package/packages/ui/src/design-system/utils.js +246 -0
- package/packages/ui/src/main.js +4 -0
- package/packages/ui/src/patterns/FeatureCard/FeatureCard.spec.md +24 -0
- package/packages/ui/src/patterns/FeatureCard/FeatureCard.types.ts +8 -0
- package/packages/ui/src/patterns/FeatureCard/FeatureCard.vue +175 -0
- package/packages/ui/src/patterns/FormField/FormField.spec.md +65 -0
- package/packages/ui/src/patterns/FormField/FormField.types.ts +11 -0
- package/packages/ui/src/patterns/FormField/FormField.vue +87 -0
- package/packages/ui/src/patterns/IdentityGlyph/IdentityGlyph.vue +61 -0
- package/packages/ui/src/patterns/IdentityGlyph/IdentityHandle.vue +58 -0
- package/packages/ui/src/patterns/IdentityGlyph/identityGlyph.types.ts +36 -0
- package/packages/ui/src/patterns/IdentityGlyph/identityGlyph.utils.ts +585 -0
- package/packages/ui/src/patterns/IdentityGlyph/index.ts +5 -0
- package/packages/ui/src/patterns/KeyValueList/KeyValueList.spec.md +28 -0
- package/packages/ui/src/patterns/KeyValueList/KeyValueList.types.ts +16 -0
- package/packages/ui/src/patterns/KeyValueList/KeyValueList.vue +50 -0
- package/packages/ui/src/patterns/LandingPage/LandingIcon.vue +90 -0
- package/packages/ui/src/patterns/LandingPage/LandingPage.spec.md +24 -0
- package/packages/ui/src/patterns/LandingPage/LandingPage.types.ts +212 -0
- package/packages/ui/src/patterns/LandingPage/LandingPage.vue +599 -0
- package/packages/ui/src/patterns/ListWorkspaceLayout/ListWorkspaceLayout.test.ts +33 -0
- package/packages/ui/src/patterns/ListWorkspaceLayout/ListWorkspaceLayout.vue +44 -0
- package/packages/ui/src/patterns/Logo/Logo.spec.md +22 -0
- package/packages/ui/src/patterns/Logo/Logo.vue +160 -0
- package/packages/ui/src/patterns/PageSurface/PageSurface.spec.md +15 -0
- package/packages/ui/src/patterns/PageSurface/PageSurface.vue +85 -0
- package/packages/ui/src/patterns/PanelChrome/PanelChrome.spec.md +39 -0
- package/packages/ui/src/patterns/PanelChrome/PanelChrome.types.ts +1 -0
- package/packages/ui/src/patterns/PanelChrome/PanelChrome.vue +187 -0
- package/packages/ui/src/patterns/PreviewPanel/PreviewPanel.spec.md +31 -0
- package/packages/ui/src/patterns/PreviewPanel/PreviewPanel.types.ts +23 -0
- package/packages/ui/src/patterns/PreviewPanel/PreviewPanel.vue +354 -0
- package/packages/ui/src/patterns/RecordList/RecordList.spec.md +35 -0
- package/packages/ui/src/patterns/RecordList/RecordList.test.ts +42 -0
- package/packages/ui/src/patterns/RecordList/RecordList.types.ts +9 -0
- package/packages/ui/src/patterns/RecordList/RecordList.utils.ts +5 -0
- package/packages/ui/src/patterns/RecordList/RecordList.vue +134 -0
- package/packages/ui/src/patterns/SectionClarifier/SectionClarifier.vue +85 -0
- package/packages/ui/src/patterns/SectionIntro/SectionIntro.spec.md +25 -0
- package/packages/ui/src/patterns/SectionIntro/SectionIntro.types.ts +7 -0
- package/packages/ui/src/patterns/SectionIntro/SectionIntro.vue +141 -0
- package/packages/ui/src/patterns/SidebarNav/SidebarNav.spec.md +34 -0
- package/packages/ui/src/patterns/SidebarNav/SidebarNav.types.ts +17 -0
- package/packages/ui/src/patterns/SidebarNav/SidebarNav.vue +110 -0
- package/packages/ui/src/patterns/SplitView/SplitView.spec.md +28 -0
- package/packages/ui/src/patterns/SplitView/SplitView.test.ts +22 -0
- package/packages/ui/src/patterns/SplitView/SplitView.types.ts +3 -0
- package/packages/ui/src/patterns/SplitView/SplitView.utils.ts +13 -0
- package/packages/ui/src/patterns/SplitView/SplitView.vue +39 -0
- package/packages/ui/src/patterns/StepList/StepList.spec.md +15 -0
- package/packages/ui/src/patterns/StepList/StepList.types.ts +4 -0
- package/packages/ui/src/patterns/StepList/StepList.vue +91 -0
- package/packages/ui/src/patterns/Verification/VerificationBadge.vue +97 -0
- package/packages/ui/src/patterns/Verification/VerificationComponents.test.ts +153 -0
- package/packages/ui/src/patterns/Verification/VerificationDetailsPanel.vue +270 -0
- package/packages/ui/src/patterns/Verification/VerificationSummary.vue +171 -0
- package/packages/ui/src/patterns/Verification/index.ts +6 -0
- package/packages/ui/src/patterns/Verification/verification.types.ts +8 -0
- package/packages/ui/src/patterns/Verification/verification.utils.test.ts +37 -0
- package/packages/ui/src/patterns/Verification/verification.utils.ts +75 -0
- package/packages/ui/src/patterns/index.ts +25 -0
- package/packages/ui/src/primitives/Accordian/Accordian.vue +11 -0
- package/packages/ui/src/primitives/Accordian/AccordianItem.vue +14 -0
- package/packages/ui/src/primitives/Accordion/Accordion.props.ts +21 -0
- package/packages/ui/src/primitives/Accordion/Accordion.spec.md +50 -0
- package/packages/ui/src/primitives/Accordion/Accordion.types.ts +4 -0
- package/packages/ui/src/primitives/Accordion/Accordion.variants.ts +12 -0
- package/packages/ui/src/primitives/Accordion/Accordion.vue +71 -0
- package/packages/ui/src/primitives/Accordion/AccordionItem.props.ts +14 -0
- package/packages/ui/src/primitives/Accordion/AccordionItem.vue +40 -0
- package/packages/ui/src/primitives/Badge/Badge.props.ts +17 -0
- package/packages/ui/src/primitives/Badge/Badge.spec.md +17 -0
- package/packages/ui/src/primitives/Badge/Badge.types.ts +15 -0
- package/packages/ui/src/primitives/Badge/Badge.variants.ts +48 -0
- package/packages/ui/src/primitives/Badge/Badge.vue +31 -0
- package/packages/ui/src/primitives/Button/Button.props.ts +29 -0
- package/packages/ui/src/primitives/Button/Button.spec.md +139 -0
- package/packages/ui/src/primitives/Button/Button.types.ts +19 -0
- package/packages/ui/src/primitives/Button/Button.variants.ts +72 -0
- package/packages/ui/src/primitives/Button/Button.vue +90 -0
- package/packages/ui/src/primitives/Card/Card.props.ts +17 -0
- package/packages/ui/src/primitives/Card/Card.spec.md +29 -0
- package/packages/ui/src/primitives/Card/Card.types.ts +12 -0
- package/packages/ui/src/primitives/Card/Card.variants.ts +27 -0
- package/packages/ui/src/primitives/Card/Card.vue +37 -0
- package/packages/ui/src/primitives/Checkbox/Checkbox.props.ts +21 -0
- package/packages/ui/src/primitives/Checkbox/Checkbox.spec.md +51 -0
- package/packages/ui/src/primitives/Checkbox/Checkbox.types.ts +4 -0
- package/packages/ui/src/primitives/Checkbox/Checkbox.variants.ts +34 -0
- package/packages/ui/src/primitives/Checkbox/Checkbox.vue +92 -0
- package/packages/ui/src/primitives/Dialog/Dialog.props.ts +29 -0
- package/packages/ui/src/primitives/Dialog/Dialog.spec.md +52 -0
- package/packages/ui/src/primitives/Dialog/Dialog.types.ts +3 -0
- package/packages/ui/src/primitives/Dialog/Dialog.variants.ts +27 -0
- package/packages/ui/src/primitives/Dialog/Dialog.vue +78 -0
- package/packages/ui/src/primitives/Drawer/Drawer.props.ts +33 -0
- package/packages/ui/src/primitives/Drawer/Drawer.spec.md +50 -0
- package/packages/ui/src/primitives/Drawer/Drawer.types.ts +5 -0
- package/packages/ui/src/primitives/Drawer/Drawer.variants.ts +35 -0
- package/packages/ui/src/primitives/Drawer/Drawer.vue +88 -0
- package/packages/ui/src/primitives/FieldMessage/FieldMessage.props.ts +17 -0
- package/packages/ui/src/primitives/FieldMessage/FieldMessage.spec.md +35 -0
- package/packages/ui/src/primitives/FieldMessage/FieldMessage.types.ts +5 -0
- package/packages/ui/src/primitives/FieldMessage/FieldMessage.variants.ts +14 -0
- package/packages/ui/src/primitives/FieldMessage/FieldMessage.vue +40 -0
- package/packages/ui/src/primitives/FileInput/FileInput.props.ts +41 -0
- package/packages/ui/src/primitives/FileInput/FileInput.types.ts +6 -0
- package/packages/ui/src/primitives/FileInput/FileInput.variants.ts +46 -0
- package/packages/ui/src/primitives/FileInput/FileInput.vue +163 -0
- package/packages/ui/src/primitives/Input/Input.props.ts +29 -0
- package/packages/ui/src/primitives/Input/Input.spec.md +79 -0
- package/packages/ui/src/primitives/Input/Input.types.ts +13 -0
- package/packages/ui/src/primitives/Input/Input.variants.ts +54 -0
- package/packages/ui/src/primitives/Input/Input.vue +99 -0
- package/packages/ui/src/primitives/Label/Label.props.ts +25 -0
- package/packages/ui/src/primitives/Label/Label.spec.md +31 -0
- package/packages/ui/src/primitives/Label/Label.types.ts +3 -0
- package/packages/ui/src/primitives/Label/Label.variants.ts +17 -0
- package/packages/ui/src/primitives/Label/Label.vue +38 -0
- package/packages/ui/src/primitives/Menu/Menu.props.ts +17 -0
- package/packages/ui/src/primitives/Menu/Menu.spec.md +38 -0
- package/packages/ui/src/primitives/Menu/Menu.types.ts +10 -0
- package/packages/ui/src/primitives/Menu/Menu.variants.ts +10 -0
- package/packages/ui/src/primitives/Menu/Menu.vue +57 -0
- package/packages/ui/src/primitives/Popover/Popover.props.ts +25 -0
- package/packages/ui/src/primitives/Popover/Popover.spec.md +49 -0
- package/packages/ui/src/primitives/Popover/Popover.types.ts +3 -0
- package/packages/ui/src/primitives/Popover/Popover.variants.ts +18 -0
- package/packages/ui/src/primitives/Popover/Popover.vue +74 -0
- package/packages/ui/src/primitives/RadioGroup/RadioGroup.props.ts +29 -0
- package/packages/ui/src/primitives/RadioGroup/RadioGroup.spec.md +50 -0
- package/packages/ui/src/primitives/RadioGroup/RadioGroup.types.ts +12 -0
- package/packages/ui/src/primitives/RadioGroup/RadioGroup.variants.ts +48 -0
- package/packages/ui/src/primitives/RadioGroup/RadioGroup.vue +87 -0
- package/packages/ui/src/primitives/Separator/Separator.props.ts +9 -0
- package/packages/ui/src/primitives/Separator/Separator.spec.md +15 -0
- package/packages/ui/src/primitives/Separator/Separator.types.ts +3 -0
- package/packages/ui/src/primitives/Separator/Separator.variants.ts +8 -0
- package/packages/ui/src/primitives/Separator/Separator.vue +23 -0
- package/packages/ui/src/primitives/Skeleton/Skeleton.props.ts +21 -0
- package/packages/ui/src/primitives/Skeleton/Skeleton.spec.md +18 -0
- package/packages/ui/src/primitives/Skeleton/Skeleton.types.ts +5 -0
- package/packages/ui/src/primitives/Skeleton/Skeleton.variants.ts +18 -0
- package/packages/ui/src/primitives/Skeleton/Skeleton.vue +37 -0
- package/packages/ui/src/primitives/Spinner/Spinner.props.ts +13 -0
- package/packages/ui/src/primitives/Spinner/Spinner.spec.md +16 -0
- package/packages/ui/src/primitives/Spinner/Spinner.types.ts +5 -0
- package/packages/ui/src/primitives/Spinner/Spinner.variants.ts +15 -0
- package/packages/ui/src/primitives/Spinner/Spinner.vue +33 -0
- package/packages/ui/src/primitives/SplitButton/SplitButton.vue +108 -0
- package/packages/ui/src/primitives/Switch/Switch.props.ts +21 -0
- package/packages/ui/src/primitives/Switch/Switch.spec.md +49 -0
- package/packages/ui/src/primitives/Switch/Switch.types.ts +3 -0
- package/packages/ui/src/primitives/Switch/Switch.variants.ts +34 -0
- package/packages/ui/src/primitives/Switch/Switch.vue +71 -0
- package/packages/ui/src/primitives/Tabs/Tabs.props.ts +25 -0
- package/packages/ui/src/primitives/Tabs/Tabs.spec.md +48 -0
- package/packages/ui/src/primitives/Tabs/Tabs.types.ts +11 -0
- package/packages/ui/src/primitives/Tabs/Tabs.variants.ts +28 -0
- package/packages/ui/src/primitives/Tabs/Tabs.vue +59 -0
- package/packages/ui/src/primitives/Textarea/Textarea.props.ts +33 -0
- package/packages/ui/src/primitives/Textarea/Textarea.spec.md +59 -0
- package/packages/ui/src/primitives/Textarea/Textarea.types.ts +5 -0
- package/packages/ui/src/primitives/Textarea/Textarea.variants.ts +27 -0
- package/packages/ui/src/primitives/Textarea/Textarea.vue +74 -0
- package/packages/ui/src/primitives/Tooltip/Tooltip.props.ts +21 -0
- package/packages/ui/src/primitives/Tooltip/Tooltip.spec.md +45 -0
- package/packages/ui/src/primitives/Tooltip/Tooltip.types.ts +3 -0
- package/packages/ui/src/primitives/Tooltip/Tooltip.variants.ts +4 -0
- package/packages/ui/src/primitives/Tooltip/Tooltip.vue +31 -0
- package/packages/ui/src/primitives/TreeView/TreeView.types.ts +10 -0
- package/packages/ui/src/primitives/TreeView/TreeView.vue +113 -0
- package/packages/ui/src/primitives/TreeView/TreeViewNode.vue +190 -0
- package/packages/ui/src/primitives/index.ts +29 -0
- package/packages/ui/src/style.css +7 -0
- package/packages/ui/src/style.js +1 -0
- package/packages/ui/src/themes/armour.css +147 -0
- package/packages/ui/src/themes/aurora.css +147 -0
- package/packages/ui/src/themes/citrine-ash.css +147 -0
- package/packages/ui/src/themes/concord.css +147 -0
- package/packages/ui/src/themes/garnet-honey.css +147 -0
- package/packages/ui/src/themes/harbor-rose.css +147 -0
- package/packages/ui/src/themes/ledger.css +147 -0
- package/packages/ui/src/themes/neon-noir.css +74 -0
- package/packages/ui/src/themes/obsidian-iris.css +147 -0
- package/packages/ui/src/themes/pixpax.css +147 -0
- package/packages/ui/src/themes/print.css +147 -0
- package/packages/ui/src/themes/prism.css +147 -0
- package/packages/ui/src/themes/proof.css +145 -0
- package/packages/ui/src/themes/semanticThemeContract.js +2256 -0
- package/packages/ui/src/themes/spruce-ink.css +147 -0
- package/packages/ui/src/themes/sunset.css +147 -0
- package/packages/ui/tailwind.config.js +64 -0
- package/packages/ui/vite.config.js +35 -0
- package/packages/ui/vite.config.js.timestamp-1780697224943-89fbc929987bc.mjs +38 -0
- package/packages/utils/CHANGELOG.md +111 -0
- package/packages/utils/README.md +3 -0
- package/packages/utils/package.json +46 -0
- package/packages/utils/src/index.test.js +39 -0
- package/packages/utils/src/index.ts +289 -0
- package/packages/utils/tsconfig.build.json +12 -0
- package/packages/utils/vite.config.js +28 -0
- package/pnpm-workspace.yaml +8 -0
- package/scripts/vite/package-lib-config.ts +59 -0
- package/tsconfig.json +24 -0
- package/tsconfig.node.json +9 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { SerializedIdentity } from "@ternent/identity";
|
|
2
|
+
|
|
3
|
+
export type ArmourIdentityInput = string | SerializedIdentity;
|
|
4
|
+
export type ArmourOutputFormat = "armor" | "binary";
|
|
5
|
+
export type ArmourBinaryInput = Uint8Array | ArrayBuffer | Blob;
|
|
6
|
+
|
|
7
|
+
export type ArmourErrorCode =
|
|
8
|
+
| "ARMOUR_INIT_FAILED"
|
|
9
|
+
| "ARMOUR_EMPTY_DATA"
|
|
10
|
+
| "ARMOUR_EMPTY_RECIPIENTS"
|
|
11
|
+
| "ARMOUR_INVALID_RECIPIENT"
|
|
12
|
+
| "ARMOUR_INVALID_SECRET_KEY"
|
|
13
|
+
| "ARMOUR_EMPTY_PASSPHRASE"
|
|
14
|
+
| "ARMOUR_DATA_TOO_LARGE"
|
|
15
|
+
| "ARMOUR_INVALID_IDENTITY"
|
|
16
|
+
| "ARMOUR_IDENTITY_DERIVATION_FAILED"
|
|
17
|
+
| "ARMOUR_ENCRYPT_FAILED"
|
|
18
|
+
| "ARMOUR_DECRYPT_FAILED";
|
|
19
|
+
|
|
20
|
+
export interface EncryptForRecipientsInput {
|
|
21
|
+
recipients: string[];
|
|
22
|
+
data: Uint8Array;
|
|
23
|
+
output?: ArmourOutputFormat;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface DecryptWithSecretKeyInput {
|
|
27
|
+
secretKey: string;
|
|
28
|
+
data: Uint8Array;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface EncryptForIdentitiesInput {
|
|
32
|
+
identities: ArmourIdentityInput[];
|
|
33
|
+
data: Uint8Array;
|
|
34
|
+
output?: ArmourOutputFormat;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface DecryptWithIdentityInput {
|
|
38
|
+
identity: ArmourIdentityInput;
|
|
39
|
+
data: Uint8Array;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface EncryptWithPassphraseInput {
|
|
43
|
+
passphrase: string;
|
|
44
|
+
data: Uint8Array;
|
|
45
|
+
output?: ArmourOutputFormat;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface DecryptWithPassphraseInput {
|
|
49
|
+
passphrase: string;
|
|
50
|
+
data: Uint8Array;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface EncryptTextForIdentitiesInput {
|
|
54
|
+
identities: ArmourIdentityInput[];
|
|
55
|
+
text: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface DecryptTextWithIdentityInput {
|
|
59
|
+
identity: ArmourIdentityInput;
|
|
60
|
+
data: string | Uint8Array;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface EncryptTextWithPassphraseInput {
|
|
64
|
+
passphrase: string;
|
|
65
|
+
text: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface DecryptTextWithPassphraseInput {
|
|
69
|
+
passphrase: string;
|
|
70
|
+
data: string | Uint8Array;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface EncryptBinaryForIdentitiesInput {
|
|
74
|
+
identities: ArmourIdentityInput[];
|
|
75
|
+
data: ArmourBinaryInput;
|
|
76
|
+
output?: ArmourOutputFormat;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface DecryptBinaryWithIdentityInput {
|
|
80
|
+
identity: ArmourIdentityInput;
|
|
81
|
+
data: ArmourBinaryInput;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface EncryptBinaryWithPassphraseInput {
|
|
85
|
+
passphrase: string;
|
|
86
|
+
data: ArmourBinaryInput;
|
|
87
|
+
output?: ArmourOutputFormat;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface DecryptBinaryWithPassphraseInput {
|
|
91
|
+
passphrase: string;
|
|
92
|
+
data: ArmourBinaryInput;
|
|
93
|
+
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { dirname, resolve } from "node:path";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
|
+
import { describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { createIdentity, serializeIdentity } from "../../identity-v2/src/index.ts";
|
|
6
|
+
|
|
7
|
+
const testDir = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const packageDir = resolve(testDir, "..");
|
|
9
|
+
|
|
10
|
+
async function loadSourceModule() {
|
|
11
|
+
vi.resetModules();
|
|
12
|
+
return import("../src/index.ts");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe("@ternent/armour", () => {
|
|
16
|
+
it("requires manual init before use", async () => {
|
|
17
|
+
const armour = await loadSourceModule();
|
|
18
|
+
|
|
19
|
+
await expect(
|
|
20
|
+
armour.encryptWithPassphrase({
|
|
21
|
+
passphrase: "correct horse battery staple",
|
|
22
|
+
data: new TextEncoder().encode("hello"),
|
|
23
|
+
}),
|
|
24
|
+
).rejects.toMatchObject({
|
|
25
|
+
name: "ArmourInitError",
|
|
26
|
+
code: "ARMOUR_INIT_FAILED",
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("initializes idempotently", async () => {
|
|
31
|
+
const armour = await loadSourceModule();
|
|
32
|
+
|
|
33
|
+
await expect(armour.initArmour()).resolves.toBeUndefined();
|
|
34
|
+
await expect(armour.initArmour()).resolves.toBeUndefined();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("derives age recipients and secrets from identity objects and strings", async () => {
|
|
38
|
+
const armour = await loadSourceModule();
|
|
39
|
+
const identity = await createIdentity("2026-03-17T00:00:00.000Z");
|
|
40
|
+
const serialized = serializeIdentity(identity);
|
|
41
|
+
|
|
42
|
+
const recipientFromObject = await armour.recipientFromIdentity(identity);
|
|
43
|
+
const recipientFromString = await armour.recipientFromIdentity(serialized);
|
|
44
|
+
const secret = await armour.secretKeyFromIdentity(identity);
|
|
45
|
+
|
|
46
|
+
expect(recipientFromObject).toMatch(/^age1/);
|
|
47
|
+
expect(recipientFromString).toBe(recipientFromObject);
|
|
48
|
+
expect(secret).toMatch(/^AGE-SECRET-KEY-1/);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("rejects malformed identity input with structured errors", async () => {
|
|
52
|
+
const armour = await loadSourceModule();
|
|
53
|
+
|
|
54
|
+
await expect(
|
|
55
|
+
armour.recipientFromIdentity('{"format":"ternent-identity","version":"nope"}'),
|
|
56
|
+
).rejects.toMatchObject({
|
|
57
|
+
name: "ArmourIdentityError",
|
|
58
|
+
code: "ARMOUR_INVALID_IDENTITY",
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("encrypts for one identity and decrypts with the same identity", async () => {
|
|
63
|
+
const armour = await loadSourceModule();
|
|
64
|
+
await armour.initArmour();
|
|
65
|
+
const identity = await createIdentity();
|
|
66
|
+
|
|
67
|
+
const ciphertext = await armour.encryptForIdentities({
|
|
68
|
+
identities: [identity],
|
|
69
|
+
data: new TextEncoder().encode("hello"),
|
|
70
|
+
});
|
|
71
|
+
const plaintext = await armour.decryptWithIdentity({
|
|
72
|
+
identity,
|
|
73
|
+
data: ciphertext,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
expect(new TextDecoder().decode(plaintext)).toBe("hello");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("encrypts for multiple identities in one pass", async () => {
|
|
80
|
+
const armour = await loadSourceModule();
|
|
81
|
+
await armour.initArmour();
|
|
82
|
+
const alice = await createIdentity();
|
|
83
|
+
const bob = await createIdentity();
|
|
84
|
+
|
|
85
|
+
const ciphertext = await armour.encryptForIdentities({
|
|
86
|
+
identities: [alice, bob],
|
|
87
|
+
data: new TextEncoder().encode("shared"),
|
|
88
|
+
});
|
|
89
|
+
const plaintext = await armour.decryptWithIdentity({
|
|
90
|
+
identity: bob,
|
|
91
|
+
data: ciphertext,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
expect(new TextDecoder().decode(plaintext)).toBe("shared");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("supports raw recipient passthrough and raw secret decryption", async () => {
|
|
98
|
+
const armour = await loadSourceModule();
|
|
99
|
+
await armour.initArmour();
|
|
100
|
+
const identity = await createIdentity();
|
|
101
|
+
const recipient = await armour.recipientFromIdentity(identity);
|
|
102
|
+
const secretKey = await armour.secretKeyFromIdentity(identity);
|
|
103
|
+
|
|
104
|
+
const ciphertext = await armour.encryptForRecipients({
|
|
105
|
+
recipients: [recipient],
|
|
106
|
+
data: new TextEncoder().encode("raw"),
|
|
107
|
+
output: "binary",
|
|
108
|
+
});
|
|
109
|
+
const plaintext = await armour.decryptWithSecretKey({
|
|
110
|
+
secretKey,
|
|
111
|
+
data: ciphertext,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
expect(new TextDecoder().decode(plaintext)).toBe("raw");
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("normalizes recipient validation failures", async () => {
|
|
118
|
+
const armour = await loadSourceModule();
|
|
119
|
+
await armour.initArmour();
|
|
120
|
+
|
|
121
|
+
await expect(
|
|
122
|
+
armour.encryptForRecipients({
|
|
123
|
+
recipients: [],
|
|
124
|
+
data: new TextEncoder().encode("hello"),
|
|
125
|
+
}),
|
|
126
|
+
).rejects.toMatchObject({
|
|
127
|
+
name: "ArmourValidationError",
|
|
128
|
+
code: "ARMOUR_EMPTY_RECIPIENTS",
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("round trips passphrase encryption and rejects wrong passphrases", async () => {
|
|
133
|
+
const armour = await loadSourceModule();
|
|
134
|
+
await armour.initArmour();
|
|
135
|
+
|
|
136
|
+
const ciphertext = await armour.encryptWithPassphrase({
|
|
137
|
+
passphrase: "correct horse battery staple",
|
|
138
|
+
data: new TextEncoder().encode("secret"),
|
|
139
|
+
});
|
|
140
|
+
const plaintext = await armour.decryptWithPassphrase({
|
|
141
|
+
passphrase: "correct horse battery staple",
|
|
142
|
+
data: ciphertext,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
expect(new TextDecoder().decode(plaintext)).toBe("secret");
|
|
146
|
+
|
|
147
|
+
await expect(
|
|
148
|
+
armour.decryptWithPassphrase({
|
|
149
|
+
passphrase: "wrong",
|
|
150
|
+
data: ciphertext,
|
|
151
|
+
}),
|
|
152
|
+
).rejects.toMatchObject({
|
|
153
|
+
name: "ArmourDecryptionError",
|
|
154
|
+
code: "ARMOUR_DECRYPT_FAILED",
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("rejects empty passphrases with structured validation errors", async () => {
|
|
159
|
+
const armour = await loadSourceModule();
|
|
160
|
+
await armour.initArmour();
|
|
161
|
+
|
|
162
|
+
await expect(
|
|
163
|
+
armour.encryptWithPassphrase({
|
|
164
|
+
passphrase: "",
|
|
165
|
+
data: new TextEncoder().encode("hello"),
|
|
166
|
+
}),
|
|
167
|
+
).rejects.toMatchObject({
|
|
168
|
+
name: "ArmourValidationError",
|
|
169
|
+
code: "ARMOUR_EMPTY_PASSPHRASE",
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("round trips text helpers", async () => {
|
|
174
|
+
const armour = await loadSourceModule();
|
|
175
|
+
await armour.initArmour();
|
|
176
|
+
const identity = await createIdentity();
|
|
177
|
+
|
|
178
|
+
const ciphertext = await armour.encryptTextForIdentities({
|
|
179
|
+
identities: [identity],
|
|
180
|
+
text: "hello utf8",
|
|
181
|
+
});
|
|
182
|
+
const plaintext = await armour.decryptTextWithIdentity({
|
|
183
|
+
identity,
|
|
184
|
+
data: ciphertext,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
expect(ciphertext).toContain("-----BEGIN AGE ENCRYPTED FILE-----");
|
|
188
|
+
expect(plaintext).toBe("hello utf8");
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("accepts ArrayBuffer, Blob, and File-compatible inputs for binary helpers", async () => {
|
|
192
|
+
const armour = await loadSourceModule();
|
|
193
|
+
await armour.initArmour();
|
|
194
|
+
const identity = await createIdentity();
|
|
195
|
+
const fileLike =
|
|
196
|
+
typeof File === "undefined"
|
|
197
|
+
? new Blob(["blob data"], { type: "text/plain" })
|
|
198
|
+
: new File(["blob data"], "demo.txt", { type: "text/plain" });
|
|
199
|
+
|
|
200
|
+
const ciphertextFromArrayBuffer = await armour.encryptBinaryForIdentities({
|
|
201
|
+
identities: [identity],
|
|
202
|
+
data: new TextEncoder().encode("buffer data").buffer,
|
|
203
|
+
output: "binary",
|
|
204
|
+
});
|
|
205
|
+
const plaintextFromArrayBuffer = await armour.decryptBinaryWithIdentity({
|
|
206
|
+
identity,
|
|
207
|
+
data: ciphertextFromArrayBuffer,
|
|
208
|
+
});
|
|
209
|
+
const ciphertextFromFile = await armour.encryptBinaryForIdentities({
|
|
210
|
+
identities: [identity],
|
|
211
|
+
data: fileLike,
|
|
212
|
+
});
|
|
213
|
+
const plaintextFromFile = await armour.decryptBinaryWithIdentity({
|
|
214
|
+
identity,
|
|
215
|
+
data: ciphertextFromFile,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
expect(new TextDecoder().decode(plaintextFromArrayBuffer)).toBe("buffer data");
|
|
219
|
+
expect(new TextDecoder().decode(plaintextFromFile)).toBe("blob data");
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it("keeps the package boundary clean", async () => {
|
|
223
|
+
const sourceFiles = [
|
|
224
|
+
resolve(packageDir, "src/index.ts"),
|
|
225
|
+
resolve(packageDir, "src/identity.ts"),
|
|
226
|
+
resolve(packageDir, "src/recipients.ts"),
|
|
227
|
+
resolve(packageDir, "src/passphrase.ts"),
|
|
228
|
+
resolve(packageDir, "src/text.ts"),
|
|
229
|
+
resolve(packageDir, "src/files.ts"),
|
|
230
|
+
];
|
|
231
|
+
const source = (await Promise.all(sourceFiles.map((file) => readFile(file, "utf8")))).join(
|
|
232
|
+
"\n",
|
|
233
|
+
);
|
|
234
|
+
const armour = await loadSourceModule();
|
|
235
|
+
const identity = await createIdentity();
|
|
236
|
+
|
|
237
|
+
expect(source).not.toMatch(/@ternent\/seal-/);
|
|
238
|
+
expect(source).not.toMatch(/\benvelope\b/i);
|
|
239
|
+
expect(source).not.toMatch(/\b(sign|verify)(Utf8|Bytes)?\b/);
|
|
240
|
+
expect(source).not.toMatch(/\bencryptText\b(?!ForIdentities|WithPassphrase)/);
|
|
241
|
+
expect(source).not.toMatch(/\bdecryptText\b(?!WithIdentity|WithPassphrase)/);
|
|
242
|
+
|
|
243
|
+
await armour.initArmour();
|
|
244
|
+
await expect(
|
|
245
|
+
armour.encryptForIdentities({
|
|
246
|
+
identities: [identity],
|
|
247
|
+
data: new TextEncoder().encode("async"),
|
|
248
|
+
}),
|
|
249
|
+
).resolves.toBeInstanceOf(Uint8Array);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it("imports from built ESM output without manual path hacks", async () => {
|
|
253
|
+
const entryUrl = pathToFileURL(resolve(packageDir, "dist/index.js")).href;
|
|
254
|
+
const armour = await import(entryUrl);
|
|
255
|
+
const identity = await createIdentity();
|
|
256
|
+
|
|
257
|
+
await armour.initArmour();
|
|
258
|
+
|
|
259
|
+
const ciphertext = await armour.encryptForIdentities({
|
|
260
|
+
identities: [identity],
|
|
261
|
+
data: new TextEncoder().encode("built package"),
|
|
262
|
+
});
|
|
263
|
+
const plaintext = await armour.decryptWithIdentity({
|
|
264
|
+
identity,
|
|
265
|
+
data: ciphertext,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
expect(new TextDecoder().decode(plaintext)).toBe("built package");
|
|
269
|
+
});
|
|
270
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": "./src",
|
|
5
|
+
"outDir": "./dist",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"emitDeclarationOnly": true
|
|
9
|
+
},
|
|
10
|
+
"include": ["src/**/*.ts"],
|
|
11
|
+
"exclude": ["src/**/*.test.*", "test", "dist", "node_modules"]
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "ES2020",
|
|
5
|
+
"module": "NodeNext",
|
|
6
|
+
"moduleResolution": "NodeNext",
|
|
7
|
+
"lib": ["ES2020", "DOM"],
|
|
8
|
+
"types": ["node", "vitest/globals"]
|
|
9
|
+
},
|
|
10
|
+
"include": ["src/**/*.ts", "test/**/*.ts", "vite.config.ts"],
|
|
11
|
+
"exclude": ["dist", "node_modules"]
|
|
12
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { dirname, resolve } from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { defineConfig } from "vite";
|
|
4
|
+
import { createPackageExternal, resolvePackageDir } from "../../scripts/vite/package-lib-config";
|
|
5
|
+
|
|
6
|
+
const configDir = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const external = createPackageExternal(resolvePackageDir(import.meta.url));
|
|
8
|
+
|
|
9
|
+
export default defineConfig({
|
|
10
|
+
build: {
|
|
11
|
+
outDir: "dist",
|
|
12
|
+
sourcemap: true,
|
|
13
|
+
target: "es2020",
|
|
14
|
+
minify: false,
|
|
15
|
+
lib: {
|
|
16
|
+
entry: resolve(configDir, "src/index.ts"),
|
|
17
|
+
name: "ternentArmour",
|
|
18
|
+
fileName: "index",
|
|
19
|
+
formats: ["es"],
|
|
20
|
+
},
|
|
21
|
+
rollupOptions: {
|
|
22
|
+
external,
|
|
23
|
+
output: {
|
|
24
|
+
format: "es",
|
|
25
|
+
entryFileNames: "index.js",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# @ternent/concord
|
|
2
|
+
|
|
3
|
+
## 0.2.9
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies []:
|
|
8
|
+
- @ternent/ledger@0.1.8
|
|
9
|
+
|
|
10
|
+
## 0.2.8
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- Updated dependencies []:
|
|
15
|
+
- @ternent/ledger@0.1.7
|
|
16
|
+
|
|
17
|
+
## 0.2.7
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- Updated dependencies []:
|
|
22
|
+
- @ternent/ledger@0.1.6
|
|
23
|
+
|
|
24
|
+
## 0.2.6
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- [`583a65c2dc6917a634019ea53c6987dc3960a8b2`](https://github.com/samternent/home/commit/583a65c2dc6917a634019ea53c6987dc3960a8b2) Thanks [@samternent](https://github.com/samternent)! - Update concord
|
|
29
|
+
|
|
30
|
+
## 0.2.5
|
|
31
|
+
|
|
32
|
+
### Patch Changes
|
|
33
|
+
|
|
34
|
+
- [`42a5b52f19d93079ca76a07f44c647d5e6866009`](https://github.com/samternent/home/commit/42a5b52f19d93079ca76a07f44c647d5e6866009) Thanks [@samternent](https://github.com/samternent)! - Align build with identity
|
|
35
|
+
|
|
36
|
+
## 0.2.4
|
|
37
|
+
|
|
38
|
+
### Patch Changes
|
|
39
|
+
|
|
40
|
+
- [`cb63d9c1dc91e0cd3924bafd5abcde89cc02992f`](https://github.com/samternent/home/commit/cb63d9c1dc91e0cd3924bafd5abcde89cc02992f) Thanks [@samternent](https://github.com/samternent)! - Fix workflows
|
|
41
|
+
|
|
42
|
+
- Updated dependencies [[`cb63d9c1dc91e0cd3924bafd5abcde89cc02992f`](https://github.com/samternent/home/commit/cb63d9c1dc91e0cd3924bafd5abcde89cc02992f)]:
|
|
43
|
+
- @ternent/ledger@0.1.5
|
|
44
|
+
|
|
45
|
+
## 0.2.3
|
|
46
|
+
|
|
47
|
+
### Patch Changes
|
|
48
|
+
|
|
49
|
+
- [`acf974919a1176da235e7ec7468c46df8bc13402`](https://github.com/samternent/home/commit/acf974919a1176da235e7ec7468c46df8bc13402) Thanks [@samternent](https://github.com/samternent)! - Update build configs
|
|
50
|
+
|
|
51
|
+
- Updated dependencies [[`acf974919a1176da235e7ec7468c46df8bc13402`](https://github.com/samternent/home/commit/acf974919a1176da235e7ec7468c46df8bc13402)]:
|
|
52
|
+
- @ternent/ledger@0.1.4
|
|
53
|
+
|
|
54
|
+
## 0.2.2
|
|
55
|
+
|
|
56
|
+
### Patch Changes
|
|
57
|
+
|
|
58
|
+
- [`3fd51c9f51ccc3d816d044aa8aa161d9683534c3`](https://github.com/samternent/home/commit/3fd51c9f51ccc3d816d044aa8aa161d9683534c3) Thanks [@samternent](https://github.com/samternent)! - alias utils
|
|
59
|
+
|
|
60
|
+
- Updated dependencies []:
|
|
61
|
+
- @ternent/ledger@0.1.3
|
|
62
|
+
|
|
63
|
+
## 0.2.1
|
|
64
|
+
|
|
65
|
+
### Patch Changes
|
|
66
|
+
|
|
67
|
+
- [`109f8c3989b16a161a34b933dcad68390d9219c5`](https://github.com/samternent/home/commit/109f8c3989b16a161a34b933dcad68390d9219c5) Thanks [@samternent](https://github.com/samternent)! - Fix deps
|
|
68
|
+
|
|
69
|
+
- Updated dependencies [[`109f8c3989b16a161a34b933dcad68390d9219c5`](https://github.com/samternent/home/commit/109f8c3989b16a161a34b933dcad68390d9219c5)]:
|
|
70
|
+
- @ternent/ledger@0.1.2
|
|
71
|
+
|
|
72
|
+
## 0.2.0
|
|
73
|
+
|
|
74
|
+
### Minor Changes
|
|
75
|
+
|
|
76
|
+
- [`92050833da54cacbd6fe33349d9b8fb66754d82c`](https://github.com/samternent/home/commit/92050833da54cacbd6fe33349d9b8fb66754d82c) Thanks [@samternent](https://github.com/samternent)! - Add the initial Concord command runtime on top of `@ternent/ledger`.
|
|
77
|
+
|
|
78
|
+
### Patch Changes
|
|
79
|
+
|
|
80
|
+
- [`92050833da54cacbd6fe33349d9b8fb66754d82c`](https://github.com/samternent/home/commit/92050833da54cacbd6fe33349d9b8fb66754d82c) Thanks [@samternent](https://github.com/samternent)! - Concord library migration
|
|
81
|
+
|
|
82
|
+
- Updated dependencies [[`92050833da54cacbd6fe33349d9b8fb66754d82c`](https://github.com/samternent/home/commit/92050833da54cacbd6fe33349d9b8fb66754d82c)]:
|
|
83
|
+
- @ternent/ledger@0.1.1
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
`@ternent/concord` is the command/replay runtime over `@ternent/ledger`.
|
|
4
|
+
|
|
5
|
+
- Do not add app-domain behavior here; keep this package runtime-generic.
|
|
6
|
+
- Replay flow is entry-first: for each entry, every plugin runs `applyEntry` in plugin order.
|
|
7
|
+
- Decrypt capability is identity-derived; plugins receive `plain`, `decrypted`, or `encrypted` replay payloads.
|
|
8
|
+
- Keep integrity strict: invalid committed history must not produce trusted runtime state.
|
|
9
|
+
- Preserve plugin contract stability (`reset/beginReplay/applyEntry/endReplay`).
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# `@ternent/concord`
|
|
2
|
+
|
|
3
|
+
Command-driven replay runtime for building non-custodial applications on top of `@ternent/ledger`.
|
|
4
|
+
|
|
5
|
+
Concord is replay-first:
|
|
6
|
+
|
|
7
|
+
- ledger history is truth
|
|
8
|
+
- replay is the primary abstraction
|
|
9
|
+
- replay plugins are the only replay consumer type
|
|
10
|
+
- state, reactivity, databases, and indexes are all projection
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { createConcordApp, type ConcordReplayPlugin } from "@ternent/concord";
|
|
14
|
+
|
|
15
|
+
const todoPlugin: ConcordReplayPlugin<{
|
|
16
|
+
items: Record<string, { id: string; title: string; completed: boolean }>;
|
|
17
|
+
}> = {
|
|
18
|
+
id: "todo",
|
|
19
|
+
initialState() {
|
|
20
|
+
return { items: {} };
|
|
21
|
+
},
|
|
22
|
+
commands: {
|
|
23
|
+
"todo.create-item": async (_ctx, input: { id: string; title: string }) => ({
|
|
24
|
+
kind: "todo.item.created",
|
|
25
|
+
payload: input,
|
|
26
|
+
}),
|
|
27
|
+
},
|
|
28
|
+
applyEntry(entry, ctx) {
|
|
29
|
+
if (entry.kind !== "todo.item.created" || entry.payload.type !== "plain") {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const payload = entry.payload.data as { id: string; title: string };
|
|
34
|
+
ctx.setState((state) => ({
|
|
35
|
+
items: {
|
|
36
|
+
...state.items,
|
|
37
|
+
[payload.id]: {
|
|
38
|
+
id: payload.id,
|
|
39
|
+
title: payload.title,
|
|
40
|
+
completed: false,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
}));
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const app = await createConcordApp({
|
|
48
|
+
identity,
|
|
49
|
+
storage,
|
|
50
|
+
plugins: [todoPlugin],
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
await app.load();
|
|
54
|
+
|
|
55
|
+
await app.command("todo.create-item", {
|
|
56
|
+
id: crypto.randomUUID(),
|
|
57
|
+
title: "Buy milk",
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
await app.commit({
|
|
61
|
+
metadata: {
|
|
62
|
+
message: "Create first todo",
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const todoState = app.getReplayState("todo");
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Identity
|
|
70
|
+
|
|
71
|
+
Concord now supports two runtime modes:
|
|
72
|
+
|
|
73
|
+
- read-only: omit `identity` and use Concord for load/import/verify/replay only
|
|
74
|
+
- interactive: provide `identity` and use Concord for signed commands and commits
|
|
75
|
+
|
|
76
|
+
Interactive mode:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
const app = await createConcordApp({
|
|
80
|
+
identity,
|
|
81
|
+
storage,
|
|
82
|
+
plugins,
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Concord derives author, signer, and decrypt capability internally. Command handlers receive the same high-level `SerializedIdentity` at `ctx.identity`. Ledger-facing identity adaptation stays private to Concord.
|
|
87
|
+
|
|
88
|
+
Read-only mode:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const app = await createConcordApp({
|
|
92
|
+
storage,
|
|
93
|
+
plugins,
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
In read-only mode:
|
|
98
|
+
|
|
99
|
+
- `load`, `importLedger`, `verify`, `replay`, and `recompute` are allowed
|
|
100
|
+
- `create`, `command`, `commit`, and `clearStaged` fail with `READ_ONLY_RUNTIME`
|
|
101
|
+
- encrypted entries remain encrypted unless an identity is present for decrypt capability
|
|
102
|
+
|
|
103
|
+
## Replay Plugins
|
|
104
|
+
|
|
105
|
+
Replay plugins are the only replay consumer type.
|
|
106
|
+
|
|
107
|
+
- a replay plugin may keep replay-derived state with `initialState()` and `ctx.setState(...)`
|
|
108
|
+
- a replay plugin may materialize into external systems like Loki, Vue refs, React stores, or IndexedDB
|
|
109
|
+
- commands remain typed entry producers
|
|
110
|
+
|
|
111
|
+
`reset` has a narrow meaning:
|
|
112
|
+
|
|
113
|
+
- `reset` prepares plugin-local replay workspace for a new replay pass
|
|
114
|
+
- `reset` does not imply clearing previously published external surfaces
|
|
115
|
+
- if a plugin needs external atomic swap behavior, that belongs to the plugin and usually happens in `endReplay`
|
|
116
|
+
|
|
117
|
+
Concord only guarantees atomicity at the published Concord state boundary. External projection atomicity is plugin-owned.
|
|
118
|
+
|
|
119
|
+
## Replay And Integrity
|
|
120
|
+
|
|
121
|
+
The core lifecycle is:
|
|
122
|
+
|
|
123
|
+
1. `command(...)` stages one or more entries
|
|
124
|
+
2. local replay reflects committed truth plus staged truth
|
|
125
|
+
3. `commit(...)` groups staged entries into a signed commit
|
|
126
|
+
4. replay rebuilds published runtime state from that history
|
|
127
|
+
|
|
128
|
+
Concord treats committed history as atomic truth. If any reachable committed byte is invalid, Concord does not present the runtime state as trustworthy.
|
|
129
|
+
|
|
130
|
+
## Partial Replay
|
|
131
|
+
|
|
132
|
+
Concord supports ranged replay options:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
await app.replay({
|
|
136
|
+
fromEntryId: "entry_a",
|
|
137
|
+
toEntryId: "entry_b",
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Two distinctions matter:
|
|
142
|
+
|
|
143
|
+
- replay of any ordered slice is deterministic
|
|
144
|
+
- authoritative full-state reconstruction still requires replay from genesis or a valid checkpoint
|
|
145
|
+
|
|
146
|
+
That distinction is intentional. Partial replay metadata exists so Concord stays compatible with future timeline scrubbing and replay-slider UX without redesigning the plugin contract.
|