@dxos/protocols 0.9.0 → 0.9.1-main.c7dcc2e112

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.
Files changed (35) hide show
  1. package/dist/src/Config2.d.ts +91 -0
  2. package/dist/src/Config2.d.ts.map +1 -0
  3. package/dist/src/Config2.js +56 -0
  4. package/dist/src/Config2.js.map +1 -0
  5. package/dist/src/edge/edge.d.ts +82 -0
  6. package/dist/src/edge/edge.d.ts.map +1 -1
  7. package/dist/src/edge/edge.js +50 -0
  8. package/dist/src/edge/edge.js.map +1 -1
  9. package/dist/src/edge/registry.d.ts +231 -144
  10. package/dist/src/edge/registry.d.ts.map +1 -1
  11. package/dist/src/edge/registry.js +126 -87
  12. package/dist/src/edge/registry.js.map +1 -1
  13. package/dist/src/index.d.ts +1 -0
  14. package/dist/src/index.d.ts.map +1 -1
  15. package/dist/src/index.js +1 -0
  16. package/dist/src/index.js.map +1 -1
  17. package/dist/src/proto/gen/dxos/client/services.d.ts +162 -162
  18. package/dist/src/proto/gen/dxos/client/services.d.ts.map +1 -1
  19. package/dist/src/proto/gen/dxos/client/services.js.map +1 -1
  20. package/dist/src/proto/gen/google/protobuf.d.ts +27 -27
  21. package/dist/src/proto/gen/google/protobuf.d.ts.map +1 -1
  22. package/dist/src/proto/gen/google/protobuf.js.map +1 -1
  23. package/dist/tsconfig.tsbuildinfo +1 -1
  24. package/package.json +10 -9
  25. package/src/Config2.ts +68 -0
  26. package/src/edge/edge.ts +62 -0
  27. package/src/edge/registry.ts +142 -101
  28. package/src/index.ts +1 -0
  29. package/src/lexicons/org.dxos.experimental/README.md +29 -0
  30. package/src/lexicons/org.dxos.experimental/package.profile.json +87 -0
  31. package/src/lexicons/org.dxos.experimental/package.release.json +45 -0
  32. package/src/lexicons/org.dxos.experimental/publisher.profile.json +37 -0
  33. package/src/lexicons/org.dxos.experimental/publisher.verification.json +36 -0
  34. package/src/proto/gen/dxos/client/services.ts +162 -162
  35. package/src/proto/gen/google/protobuf.ts +27 -27
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/protocols",
3
- "version": "0.9.0",
3
+ "version": "0.9.1-main.c7dcc2e112",
4
4
  "description": "Protobuf definitions for DXOS protocols.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -37,7 +37,8 @@
37
37
  "./buf/*": {
38
38
  "browser": "./dist/src/buf/proto/gen/*.js",
39
39
  "import": "./dist/src/buf/proto/gen/*.js"
40
- }
40
+ },
41
+ "./lexicons/*": "./src/lexicons/*"
41
42
  },
42
43
  "types": "dist/src/index.d.ts",
43
44
  "files": [
@@ -46,18 +47,18 @@
46
47
  ],
47
48
  "dependencies": {
48
49
  "@bufbuild/protobuf": "2.11.0",
49
- "@dxos/codec-protobuf": "0.9.0",
50
- "@dxos/effect": "0.9.0",
51
- "@dxos/errors": "0.9.0",
52
- "@dxos/invariant": "0.9.0",
53
- "@dxos/keys": "0.9.0",
54
- "@dxos/timeframe": "0.9.0"
50
+ "@dxos/codec-protobuf": "0.9.1-main.c7dcc2e112",
51
+ "@dxos/effect": "0.9.1-main.c7dcc2e112",
52
+ "@dxos/errors": "0.9.1-main.c7dcc2e112",
53
+ "@dxos/invariant": "0.9.1-main.c7dcc2e112",
54
+ "@dxos/keys": "0.9.1-main.c7dcc2e112",
55
+ "@dxos/timeframe": "0.9.1-main.c7dcc2e112"
55
56
  },
56
57
  "devDependencies": {
57
58
  "@bufbuild/buf": "1.65.0",
58
59
  "@bufbuild/protoc-gen-es": "2.11.0",
59
60
  "effect": "3.21.3",
60
- "@dxos/protobuf-compiler": "0.9.0"
61
+ "@dxos/protobuf-compiler": "0.9.1-main.c7dcc2e112"
61
62
  },
62
63
  "peerDependencies": {
63
64
  "effect": "3.21.3"
package/src/Config2.ts ADDED
@@ -0,0 +1,68 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ // @import-as-namespace
6
+
7
+ import * as Schema from 'effect/Schema';
8
+
9
+ /** A plugin preview image with optional theme-specific URLs (`light` / `dark`). */
10
+ export const Screenshot = Schema.Struct({
11
+ light: Schema.optional(Schema.String),
12
+ dark: Schema.optional(Schema.String),
13
+ });
14
+ export type Screenshot = Schema.Schema.Type<typeof Screenshot>;
15
+
16
+ /** Icon reference: Phosphor icon key with an optional theme hue. */
17
+ export const Icon = Schema.Struct({
18
+ key: Schema.String.pipe(Schema.nonEmptyString()),
19
+ hue: Schema.optional(Schema.String),
20
+ });
21
+ export type Icon = Schema.Schema.Type<typeof Icon>;
22
+
23
+ export const Plugin = Schema.Struct({
24
+ /** Reverse-domain NSID — the plugin's globally-unique key (e.g. `org.dxos.plugin.excalidraw`). */
25
+ key: Schema.String.pipe(Schema.nonEmptyString()),
26
+ name: Schema.String.pipe(Schema.nonEmptyString()),
27
+ description: Schema.optional(Schema.String),
28
+ /**
29
+ * Author or organization name. Only used for bundled plugins. For plugins published to the
30
+ * registry this field is ignored — the verified publisher (handle ?? did) is used instead.
31
+ */
32
+ author: Schema.optional(Schema.String),
33
+ homePage: Schema.optional(Schema.String),
34
+ source: Schema.optional(Schema.String),
35
+ screenshots: Schema.optional(Schema.Array(Screenshot)),
36
+ tags: Schema.optional(Schema.Array(Schema.String)),
37
+ icon: Schema.optional(Icon),
38
+ spec: Schema.optional(Schema.String),
39
+ /** Composer plugin ids this plugin depends on at runtime (NSIDs). */
40
+ dependsOn: Schema.optional(Schema.Array(Schema.String)),
41
+ });
42
+ export type Plugin = Schema.Schema.Type<typeof Plugin>;
43
+
44
+ /**
45
+ * Publish orchestration for `dx registry publish`: build command, output directory, and optional
46
+ * hosting override. All three are consumed together — they are a single workflow, not separate concerns.
47
+ */
48
+ export const Publish = Schema.Struct({
49
+ buildCommand: Schema.optional(Schema.String),
50
+ outputDirectory: Schema.optional(Schema.String),
51
+ assetBaseUrl: Schema.optional(Schema.String),
52
+ });
53
+ export type Publish = Schema.Schema.Type<typeof Publish>;
54
+
55
+ /**
56
+ * The `dx.config.ts` schema: the TypeScript-authored replacement for the
57
+ * plugin section of `dx.yml`. v1 carries one plugin's self-declared `meta` plus optional publish
58
+ * orchestration; it will grow to absorb the rest of the (currently proto-based) config over time and
59
+ * eventually become the canonical config.
60
+ */
61
+ export const Config = Schema.Struct({
62
+ plugin: Plugin,
63
+ publish: Schema.optional(Publish),
64
+ });
65
+ export type Config = Schema.Schema.Type<typeof Config>;
66
+
67
+ /** Identity helper: authors a typed `dx.config.ts`. Validation runs at load time, not here. */
68
+ export const make = (config: Config): Config => config;
package/src/edge/edge.ts CHANGED
@@ -339,6 +339,9 @@ export enum OAuthProvider {
339
339
  TRELLO = 'trello',
340
340
  }
341
341
 
342
+ /** atproto OAuth scopes for the Atmosphere integration and account-recovery flows. */
343
+ export const ATPROTO_OAUTH_SCOPES = ['atproto', 'transition:generic', 'transition:email'] as const;
344
+
342
345
  export const InitiateOAuthFlowRequestSchema = Schema.Struct({
343
346
  provider: Schema.Enums(OAuthProvider),
344
347
  spaceId: Schema.String.pipe(Schema.filter(SpaceId.isValid)), // TODO(burdon): Use SpaceId.
@@ -761,6 +764,65 @@ export const RequestAccessRequestSchema = Schema.Struct({
761
764
  export type RequestAccessRequest = Schema.Schema.Type<typeof RequestAccessRequestSchema>;
762
765
  export type RequestAccessResponse = { received: boolean };
763
766
 
767
+ //
768
+ // Metering (VP auth)
769
+ //
770
+
771
+ /** Structured usage key aligned with {@link MeteringLimitSchema} (without cap/window fields). */
772
+ export const MeteringUsageKeySchema = Schema.Struct({
773
+ /** Event type (e.g. `ai`). */
774
+ eventType: Schema.String,
775
+ /** Value key being metered (e.g. `outputTokens`). */
776
+ valueKey: Schema.String,
777
+ /**
778
+ * Positional match against the event's subtype segments (e.g. model); `*` matches any
779
+ * segment, and positions beyond the pattern are unconstrained.
780
+ */
781
+ subtypePattern: Schema.Array(Schema.String),
782
+ });
783
+ export type MeteringUsageKey = Schema.Schema.Type<typeof MeteringUsageKeySchema>;
784
+
785
+ /** Rolling-window usage total for a structured key. */
786
+ export const MeteringUsageItemSchema = Schema.Struct({
787
+ ...MeteringUsageKeySchema.fields,
788
+ amount: Schema.Number,
789
+ });
790
+ export type MeteringUsageItem = Schema.Schema.Type<typeof MeteringUsageItemSchema>;
791
+
792
+ /** A single raw usage bucket (1h resolution) for a structured key. */
793
+ export const MeteringUsageBucketSchema = Schema.Struct({
794
+ ...MeteringUsageKeySchema.fields,
795
+ /** Bucket start timestamp (epoch ms, floored to the hour). */
796
+ bucketStart: Schema.Number,
797
+ amount: Schema.Number,
798
+ });
799
+ export type MeteringUsageBucket = Schema.Schema.Type<typeof MeteringUsageBucketSchema>;
800
+
801
+ export const MeteringLimitSchema = Schema.Struct({
802
+ /** Event type the limit applies to (e.g. `ai`). */
803
+ eventType: Schema.String,
804
+ /** Value key being limited (e.g. `outputTokens`). */
805
+ valueKey: Schema.String,
806
+ /**
807
+ * Positional match against the event's subtype segments (e.g. model); `*` matches any
808
+ * segment, and positions beyond the pattern are unconstrained.
809
+ */
810
+ subtypePattern: Schema.Array(Schema.String),
811
+ /** Rolling-window cap; `null` means unlimited. */
812
+ limit: Schema.NullOr(Schema.Number),
813
+ /** Window duration in seconds. */
814
+ windowDuration: Schema.Number,
815
+ });
816
+ export type MeteringLimit = Schema.Schema.Type<typeof MeteringLimitSchema>;
817
+
818
+ export const GetProfileUsageResponseSchema = Schema.Struct({
819
+ profileId: Schema.String,
820
+ usage: Schema.Array(MeteringUsageItemSchema),
821
+ limits: Schema.Array(MeteringLimitSchema),
822
+ buckets: Schema.Array(MeteringUsageBucketSchema),
823
+ });
824
+ export type GetProfileUsageResponse = Schema.Schema.Type<typeof GetProfileUsageResponseSchema>;
825
+
764
826
  //
765
827
  // Admin (X-API-KEY)
766
828
  //
@@ -4,33 +4,15 @@
4
4
 
5
5
  import * as Schema from 'effect/Schema';
6
6
 
7
- /**
8
- * Hydrated plugin metadata, mirroring the @dxos/app-framework `Plugin.Meta` shape.
9
- *
10
- * Defined locally rather than imported from @dxos/app-framework to keep the protocols
11
- * package free of UI/runtime dependencies. Consumers can treat decoded values as
12
- * `Plugin.Meta` directly.
13
- */
14
- export const PluginMetaSchema = Schema.Struct({
15
- id: Schema.String.pipe(Schema.nonEmptyString()),
16
- name: Schema.String.pipe(Schema.nonEmptyString()),
17
- description: Schema.optional(Schema.String),
18
- author: Schema.optional(Schema.String),
19
- homePage: Schema.optional(Schema.String),
20
- source: Schema.optional(Schema.String),
21
- screenshots: Schema.optional(Schema.Array(Schema.String)),
22
- tags: Schema.optional(Schema.Array(Schema.String)),
23
- icon: Schema.optional(Schema.String),
24
- iconHue: Schema.optional(Schema.String),
25
- });
26
- export type PluginMeta = Schema.Schema.Type<typeof PluginMetaSchema>;
7
+ import * as Config2 from '../Config2.ts';
27
8
 
28
9
  /**
29
- * Health signal the registry service attaches to a hydrated entry when a refresh fails.
30
- * Clients surface the appropriate badge or filter entries based on this field.
10
+ * A snapshot of a plugin's declared dependencies resolved to concrete installed versions at build
11
+ * time (`{ "@dxos/app-framework": "0.8.3", "react": "19.2.0", }`). The host derives SDK
12
+ * compatibility from the subset it shares with the plugin (the externalized `@dxos/*` packages).
31
13
  */
32
- export const PluginHealthSchema = Schema.Literal('ok', 'release-missing', 'manifest-invalid', 'repo-unavailable');
33
- export type PluginHealth = Schema.Schema.Type<typeof PluginHealthSchema>;
14
+ export const DependencyMapSchema = Schema.Record({ key: Schema.String, value: Schema.String });
15
+ export type DependencyMap = Schema.Schema.Type<typeof DependencyMapSchema>;
34
16
 
35
17
  /**
36
18
  * Filename of the entry module every plugin must publish at the root of its bundle.
@@ -50,7 +32,7 @@ export const PLUGIN_ENTRY_FILENAME = 'index.mjs';
50
32
  * Paths in `assets` are relative to the manifest's URL.
51
33
  */
52
34
  export const PluginManifestSchema = Schema.Struct({
53
- ...PluginMetaSchema.fields,
35
+ ...Config2.Plugin.fields,
54
36
  /** Plugin version (semver). Sourced from the publishing project's `package.json`. */
55
37
  version: Schema.String.pipe(Schema.nonEmptyString()),
56
38
  /**
@@ -58,107 +40,166 @@ export const PluginManifestSchema = Schema.Struct({
58
40
  * Must include {@link PLUGIN_ENTRY_FILENAME}; consumers verify on parse.
59
41
  */
60
42
  assets: Schema.Array(Schema.String).pipe(Schema.minItems(1)),
43
+ /** Declared dependencies resolved to installed versions at build time (SDK-compat source). */
44
+ dependencies: Schema.optional(DependencyMapSchema),
61
45
  });
62
46
  export type PluginManifest = Schema.Schema.Type<typeof PluginManifestSchema>;
63
47
 
48
+ // ─── ATProto-native registry view ────────────────────────────────────────────
49
+
64
50
  /**
65
- * Single hydrated plugin entry returned by the registry service.
51
+ * A single installable release of a plugin, projected from a `package.release`
52
+ * ATProto record.
66
53
  */
67
- export const PluginEntrySchema = Schema.Struct({
68
- /** GitHub repository in `owner/name` form. Empty string for entries sourced from a `manifestUrl`. */
69
- repo: Schema.String,
70
- /** Plugin metadata from the repo's latest-release `manifest.json`. */
71
- meta: PluginMetaSchema,
54
+ export const PluginReleaseSchema = Schema.Struct({
55
+ /** Semver version string, e.g. `0.8.3`. */
56
+ version: Schema.String.pipe(Schema.nonEmptyString()),
57
+ /** URL the host dynamic-imports to install this specific version. */
58
+ moduleUrl: Schema.String.pipe(Schema.nonEmptyString()),
72
59
  /**
73
- * URL of the plugin's `manifest.json`. Composer's URL loader fetches this, eagerly caches
74
- * every declared asset via the platform `PluginAssetCache`, then dynamic-imports the entry.
75
- * The URL must be CORS-safe and have its declared assets reachable as siblings.
60
+ * Dependencies this release was built against, resolved to installed versions. The host derives
61
+ * SDK compatibility from the `@dxos/*` subset to decide whether to offer this release.
76
62
  */
77
- moduleUrl: Schema.String,
78
- /** Release tag the entry was resolved from (e.g. `v0.1.0`). Empty string for `manifestUrl` entries. */
79
- releaseTag: Schema.String,
80
- /** Health signal set by the service when an entry fails to refresh. */
81
- health: PluginHealthSchema,
82
- /** Unix ms when this entry was last successfully hydrated. */
83
- hydratedAt: Schema.Number,
63
+ dependencies: Schema.optional(DependencyMapSchema),
84
64
  });
85
- export type PluginEntry = Schema.Schema.Type<typeof PluginEntrySchema>;
65
+ export type PluginRelease = Schema.Schema.Type<typeof PluginReleaseSchema>;
86
66
 
87
67
  /**
88
- * Response body of `GET /registry/plugins`.
68
+ * Verbatim content of a `package.profile` ATProto record. `key` is the record's rkey (a reverse-domain
69
+ * NSID), denormalized into the body. Version-independent identity + display metadata; provenance
70
+ * (`author`) is resolved separately from the publisher DID/handle.
89
71
  */
90
- export const GetPluginsResponseBodySchema = Schema.Struct({
91
- /** Wire-format schema version, pinned to 1. */
92
- version: Schema.Literal(1),
93
- /** Hydrated entries. Order matches the order in the upstream catalog manifest. */
94
- plugins: Schema.Array(PluginEntrySchema),
95
- /** Unix ms timestamp of the most recent successful refresh cycle. */
96
- refreshedAt: Schema.Number,
72
+ export const PluginProfileSchema = Schema.Struct({
73
+ /** Reverse-domain NSID the plugin's globally-unique key and the `package.profile` rkey (e.g. `org.dxos.plugin.excalidraw`). */
74
+ key: Schema.String.pipe(Schema.nonEmptyString()),
75
+ /** Plugin display name. */
76
+ name: Schema.String.pipe(Schema.nonEmptyString()),
77
+ /** Short description of plugin functionality. */
78
+ description: Schema.optional(Schema.String),
79
+ /** Publisher's homepage or plugin documentation URL. */
80
+ homePage: Schema.optional(Schema.String),
81
+ /** Source repository URL. */
82
+ source: Schema.optional(Schema.String),
83
+ /** Tags to help categorize the plugin. */
84
+ tags: Schema.optional(Schema.Array(Schema.String)),
85
+ /** Preview images — theme-keyed screenshot URLs shown on the plugin's card. */
86
+ screenshots: Schema.optional(Schema.Array(Config2.Screenshot)),
87
+ /** Icon identifier resolvable by `@ch-ui/icons` (e.g. `ph--sparkle--regular`), with an optional palette hue. */
88
+ icon: Schema.optional(Config2.Icon),
89
+ /** Composer plugin ids this plugin depends on at runtime (NSIDs). Author-declared, version-independent. */
90
+ dependsOn: Schema.optional(Schema.Array(Schema.String)),
91
+ /** Relative path inside the package to a bundled MDL spec (consumed by plugin-code). */
92
+ spec: Schema.optional(Schema.String),
97
93
  });
98
- export type GetPluginsResponseBody = Schema.Schema.Type<typeof GetPluginsResponseBodySchema>;
94
+ export type PluginProfile = Schema.Schema.Type<typeof PluginProfileSchema>;
99
95
 
100
96
  /**
101
- * A single published version of a plugin, as returned by the registry service's
102
- * versions endpoint. Sourced from the publishing project's GitHub releases.
97
+ * A single hydrated plugin entry returned by `GET /registry/plugins`.
103
98
  *
104
- * The host treats `tag` opaquely; ordering newest first is the service's
105
- * responsibility (typically reverse-chronological by release date).
99
+ * This is an indexer-assembled *view*analogous to emdash's `PackageView` projected
100
+ * from four ATProto record types: `package.profile`, `package.release`,
101
+ * `publisher.profile`, and `publisher.verification`. It is NOT a direct serialization
102
+ * of any single ATProto record.
103
+ *
104
+ * Design notes:
105
+ * - `profile.key` is required to be a valid NSID (e.g. `org.dxos.plugin.excalidraw`), making it
106
+ * the single identifier for both PDS addressing and the composer runtime plugin id.
107
+ * `DXN.make(profile.key, latestVersion)` reconstructs the canonical plugin DXN.
108
+ * - `releases` inlines all known versions, eliminating a separate versions round-trip for
109
+ * the version picker. Ordered newest-first.
110
+ * - `latestVersion` is a convenience pointer into `releases` indicating the recommended
111
+ * install target.
106
112
  */
107
- export const PluginVersionSchema = Schema.Struct({
108
- /** Release tag (e.g. `v0.1.21`). Stable identifier for this version. */
109
- tag: Schema.String.pipe(Schema.nonEmptyString()),
113
+ export const PluginViewSchema = Schema.Struct({
114
+ // ── Addressing / provenance (indexer-derived) ────────────────────────────
110
115
  /**
111
- * URL of this version's `manifest.json`. Matches the shape of {@link PluginEntrySchema.fields.moduleUrl}
112
- * but pinned to a specific release rather than the latest. Composer's URL loader fetches
113
- * this when the user installs / rolls back to this version.
116
+ * `at://` URI of the source `package.profile` record.
117
+ * Globally unique and stable never changes after the record is published.
114
118
  */
115
- moduleUrl: Schema.String.pipe(Schema.nonEmptyString()),
116
- /** Unix ms when the version was released. Optional services that lack this signal can omit. */
117
- releasedAt: Schema.optional(Schema.Number),
119
+ uri: Schema.String.pipe(Schema.nonEmptyString()),
120
+ /** Publisher DID, e.g. `did:plc:abc…`. Cryptographic identity; never changes. */
121
+ did: Schema.String.pipe(Schema.nonEmptyString()),
122
+ /**
123
+ * Publisher AT Protocol handle at index time, e.g. `alice.bsky.social`.
124
+ * Display-only — handles can be reassigned; never use as a stable key.
125
+ */
126
+ handle: Schema.optional(Schema.String),
127
+ /** Unix ms when the indexer last assembled this entry. */
128
+ indexedAt: Schema.Number,
129
+ /**
130
+ * Trust labels derived from curator `publisher.verification` records.
131
+ * An empty array means the entry has no verification signal.
132
+ */
133
+ labels: Schema.Array(Schema.String),
134
+
135
+ // ── Verbatim profile record content ─────────────────────────────────────
136
+ profile: PluginProfileSchema,
137
+
138
+ // ── Releases (projected from package.release records) ───────────────────
139
+ /**
140
+ * All known releases for this package, ordered newest-first.
141
+ * Powers the version picker directly — no separate endpoint needed.
142
+ */
143
+ releases: Schema.Array(PluginReleaseSchema),
144
+ /**
145
+ * The latest (recommended) release version. Always references an entry in `releases`.
146
+ * Used by the host to determine whether an update is available.
147
+ */
148
+ latestVersion: Schema.String.pipe(Schema.nonEmptyString()),
118
149
  });
119
- export type PluginVersion = Schema.Schema.Type<typeof PluginVersionSchema>;
150
+ export type PluginView = Schema.Schema.Type<typeof PluginViewSchema>;
120
151
 
121
152
  /**
122
- * Response body of `GET /registry/plugins/:repo/versions`.
123
- *
124
- * `:repo` is the GitHub `owner/name` form, URL-encoded (so `/` is escaped).
125
- * Returns all hydratable releases for the repo, newest first; clients display them
126
- * in the version picker for install / roll-back. Unauthenticated; same surface area
127
- * as `GET /registry/plugins`.
153
+ * Response body of `GET /registry/plugins`.
128
154
  */
129
- export const GetPluginVersionsResponseBodySchema = Schema.Struct({
130
- /** Wire-format schema version, pinned to 1. */
131
- version: Schema.Literal(1),
132
- /** Available versions for the requested repo, newest first. */
133
- versions: Schema.Array(PluginVersionSchema),
134
- /** Unix ms timestamp of the most recent successful refresh cycle for this repo. */
155
+ export const GetPluginsResponseBodySchema = Schema.Struct({
156
+ /** Wire-format schema version, pinned to 2. */
157
+ version: Schema.Literal(2),
158
+ /** Hydrated entries. */
159
+ plugins: Schema.Array(PluginViewSchema),
160
+ /** Unix ms timestamp of the most recent successful index cycle. */
135
161
  refreshedAt: Schema.Number,
136
162
  });
137
- export type GetPluginVersionsResponseBody = Schema.Schema.Type<typeof GetPluginVersionsResponseBodySchema>;
163
+ export type GetPluginsResponseBody = Schema.Schema.Type<typeof GetPluginsResponseBodySchema>;
138
164
 
139
- /**
140
- * A catalog entry. Two flavours:
141
- * - `{ repo }`: the registry service hydrates from the GitHub repo's latest release.
142
- * Used by the production catalog.
143
- * - `{ manifestUrl }`: the registry service skips GitHub and fetches the manifest directly.
144
- * Used for local development against an in-progress plugin (e.g. served by `vite preview`)
145
- * so authors can iterate without publishing a release.
146
- */
147
- export const RegistryEntrySchema = Schema.Union(
148
- Schema.Struct({ repo: Schema.String.pipe(Schema.nonEmptyString()) }),
149
- Schema.Struct({ manifestUrl: Schema.String.pipe(Schema.nonEmptyString()) }),
150
- );
151
- export type RegistryEntry = Schema.Schema.Type<typeof RegistryEntrySchema>;
165
+ // ─── Publisher records ────────────────────────────────────────────────────────
166
+
167
+ /** Content of a `publisher.profile` ATProto record. Display metadata for a publisher DID. */
168
+ export const PublisherProfileSchema = Schema.Struct({
169
+ displayName: Schema.String.pipe(Schema.nonEmptyString()),
170
+ bio: Schema.optional(Schema.String),
171
+ homepageUrl: Schema.optional(Schema.String),
172
+ contact: Schema.optional(Schema.String),
173
+ });
174
+ export type PublisherProfile = Schema.Schema.Type<typeof PublisherProfileSchema>;
152
175
 
153
176
  /**
154
- * Shape of the catalog manifest published in the upstream community-plugins repo.
155
- * Extra keys (e.g. `$schema`) are permitted.
177
+ * Content of a `publisher.verification` record written by the curator DID.
178
+ * Links a publisher DID to a trusted AT Protocol handle.
156
179
  */
157
- export const RegistryManifestSchema = Schema.Struct(
158
- {
159
- version: Schema.Literal(1),
160
- plugins: Schema.Array(RegistryEntrySchema),
161
- },
162
- Schema.Record({ key: Schema.String, value: Schema.Unknown }),
163
- );
164
- export type RegistryManifest = Schema.Schema.Type<typeof RegistryManifestSchema>;
180
+ export const PublisherVerificationSchema = Schema.Struct({
181
+ subject: Schema.String.pipe(Schema.nonEmptyString()),
182
+ handle: Schema.String.pipe(Schema.nonEmptyString()),
183
+ displayName: Schema.String.pipe(Schema.nonEmptyString()),
184
+ createdAt: Schema.String.pipe(Schema.nonEmptyString()),
185
+ });
186
+ export type PublisherVerification = Schema.Schema.Type<typeof PublisherVerificationSchema>;
187
+
188
+ // ─── NSID constants ───────────────────────────────────────────────────────────
189
+
190
+ /** NSID constants for the four `org.dxos.experimental.*` record collections. */
191
+ export const NSID = {
192
+ PackageProfile: 'org.dxos.experimental.package.profile',
193
+ PackageRelease: 'org.dxos.experimental.package.release',
194
+ PublisherProfile: 'org.dxos.experimental.publisher.profile',
195
+ PublisherVerification: 'org.dxos.experimental.publisher.verification',
196
+ } as const;
197
+
198
+ export type RegistryNsid = (typeof NSID)[keyof typeof NSID];
199
+
200
+ export const ALL_NSIDS: readonly RegistryNsid[] = [
201
+ NSID.PackageProfile,
202
+ NSID.PackageRelease,
203
+ NSID.PublisherProfile,
204
+ NSID.PublisherVerification,
205
+ ];
package/src/index.ts CHANGED
@@ -11,6 +11,7 @@ export * from './profile-archive.ts';
11
11
  export * from './space-archive.ts';
12
12
  export * from './storage.ts';
13
13
  export type * from './types.ts';
14
+ export * as Config2 from './Config2.ts';
14
15
  export * as FunctionProtocol from './FunctionProtocol.ts';
15
16
  export * as FeedProtocol from './FeedProtocol.ts';
16
17
  export * as TraceProtocol from './TraceProtocol.ts';
@@ -0,0 +1,29 @@
1
+ # `org.dxos.experimental.*` lexicons
2
+
3
+ Experimental AT Protocol lexicons for the DXOS plugin registry. The `experimental` segment is intentional — these schemas are unstable and will be renamed to a final namespace before promotion.
4
+
5
+ ## Records
6
+
7
+ | NSID | rkey | Purpose |
8
+ | ---------------------------------------------- | ------------------ | ----------------------------------------------------- |
9
+ | `org.dxos.experimental.package.profile` | `<key>` | Mutable package metadata (one per key per DID). |
10
+ | `org.dxos.experimental.package.release` | `<key>:<version>` | Versioned artifact record (conceptually single-write).|
11
+ | `org.dxos.experimental.publisher.profile` | `self` | Identity-level publisher metadata. |
12
+ | `org.dxos.experimental.publisher.verification` | `<verified DID>` | Trust attestation about a publisher. |
13
+
14
+ ## Trust model
15
+
16
+ `publisher.verification` records are public and unrestricted — anyone may author one. Trust is an indexing policy, not a write gate. Today the indexer honors verification records **only** from a single configured verifier DID (`REGISTRY_CURATOR_DID`) and indexes a publisher's records only if that verifier has vouched for the author DID. Revocation is achieved by deleting the verification record — the indexer unindexes that publisher on the next backfill sweep. This single-verifier gate is intended to generalize to multiple verifiers / moderation labels (à la Bluesky labelers) once that surface exists.
17
+
18
+ See `packages/sdk/app-framework/docs/registry-spec.md` for the full design, including the integrity / tamper-detection model.
19
+
20
+ ## Prior art
21
+
22
+ These lexicons are modeled on the emdash RFC:
23
+
24
+ - RFC: [emdash-cms/emdash#694](https://github.com/emdash-cms/emdash/pull/694) — design rationale, trust model trade-offs, rkey conventions, and the `experimental` namespace strategy.
25
+ - Implementation: [emdash-cms/emdash#923](https://github.com/emdash-cms/emdash/pull/923) — client, CLI, and lexicon bindings.
26
+
27
+ Differences from emdash's `com.emdashcms.experimental.*`:
28
+
29
+ - `releaseExtension` / `declaredAccess` records (sandbox manifest, granular permissions) are **not** included in v0; they will be added in a follow-on once the runtime sandbox surface is defined.
@@ -0,0 +1,87 @@
1
+ {
2
+ "lexicon": 1,
3
+ "id": "org.dxos.experimental.package.profile",
4
+ "defs": {
5
+ "main": {
6
+ "type": "record",
7
+ "key": "literal:key",
8
+ "description": "Mutable metadata for a discoverable plugin/module published on the DXOS experimental registry. The rkey is the plugin key (reverse-domain NSID, e.g. org.dxos.plugin.excalidraw). Companion org.dxos.experimental.package.release records carry versioned artifacts.",
9
+ "record": {
10
+ "type": "object",
11
+ "required": ["key", "name", "createdAt"],
12
+ "properties": {
13
+ "key": {
14
+ "type": "string",
15
+ "description": "Reverse-domain NSID — the plugin's globally-unique key and the profile rkey (e.g. org.dxos.plugin.excalidraw).",
16
+ "maxLength": 63,
17
+ "minLength": 1
18
+ },
19
+ "name": {
20
+ "type": "string",
21
+ "description": "Human-readable display name.",
22
+ "maxLength": 128
23
+ },
24
+ "description": {
25
+ "type": "string",
26
+ "description": "Short description shown in registry listings.",
27
+ "maxLength": 512
28
+ },
29
+ "homePage": {
30
+ "type": "string",
31
+ "format": "uri",
32
+ "description": "Optional canonical homepage."
33
+ },
34
+ "source": {
35
+ "type": "string",
36
+ "format": "uri",
37
+ "description": "Optional public source repository URL."
38
+ },
39
+ "tags": {
40
+ "type": "array",
41
+ "description": "Optional discovery tags.",
42
+ "maxLength": 16,
43
+ "items": { "type": "string", "maxLength": 32 }
44
+ },
45
+ "screenshots": {
46
+ "type": "array",
47
+ "description": "Optional preview images. Each item has optional light/dark theme URLs.",
48
+ "maxLength": 8,
49
+ "items": {
50
+ "type": "object",
51
+ "properties": {
52
+ "light": { "type": "string", "format": "uri" },
53
+ "dark": { "type": "string", "format": "uri" }
54
+ }
55
+ }
56
+ },
57
+ "icon": {
58
+ "type": "object",
59
+ "description": "Optional icon reference: Phosphor icon key with an optional theme hue.",
60
+ "required": ["key"],
61
+ "properties": {
62
+ "key": {
63
+ "type": "string",
64
+ "description": "Phosphor icon name (e.g. ph--compass-tool--regular).",
65
+ "maxLength": 64
66
+ },
67
+ "hue": { "type": "string", "description": "Theme hue hint (e.g. blue, amber).", "maxLength": 32 }
68
+ }
69
+ },
70
+ "dependsOn": {
71
+ "type": "array",
72
+ "description": "Optional list of plugin NSIDs this plugin depends on at runtime.",
73
+ "items": { "type": "string" }
74
+ },
75
+ "spec": {
76
+ "type": "string",
77
+ "description": "Optional relative path inside the package to a bundled MDL spec."
78
+ },
79
+ "createdAt": {
80
+ "type": "string",
81
+ "format": "datetime"
82
+ }
83
+ }
84
+ }
85
+ }
86
+ }
87
+ }