@rizom/ops 0.2.0-alpha.243 → 0.2.0-alpha.245

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.
@@ -0,0 +1,11 @@
1
+ import { type CohortConfig, type PilotConfig } from "./schema";
2
+ /** Offline-only conversion. Active desired-state loading never calls this. */
3
+ export declare function migrateLegacyPilotConfig(input: unknown): PilotConfig;
4
+ /** Offline-only cohort conversion. */
5
+ export declare function migrateLegacyCohortConfig(input: unknown): CohortConfig;
6
+ /** Offline-only YAML rewrite that retains comments while changing the schema. */
7
+ export declare function migrateLegacyPilotYaml(input: string): string;
8
+ /** Offline-only cohort rewrite that retains comments while changing selection. */
9
+ export declare function migrateLegacyCohortYaml(input: string): string;
10
+ export declare function renderPilotConfig(config: PilotConfig): string;
11
+ export declare function renderCohortConfig(config: CohortConfig): string;
@@ -1,4 +1,4 @@
1
- import { type PilotConfig, type PilotPreset } from "./schema";
1
+ import { type PilotConfig, type CanonicalBundleId } from "./schema";
2
2
  export type ExternalStatus = "unknown" | "ready" | "failed";
3
3
  export type SnapshotStatus = "present" | "missing";
4
4
  export interface ObservedUserStatus {
@@ -11,7 +11,9 @@ export interface ResolvedCohort {
11
11
  id: string;
12
12
  members: string[];
13
13
  brainVersionOverride?: string;
14
- presetOverride?: PilotPreset;
14
+ bundlesOverride?: CanonicalBundleId[];
15
+ addOverride?: string[];
16
+ removeOverride?: string[];
15
17
  aiApiKeyOverride?: string;
16
18
  gitSyncTokenOverride?: string;
17
19
  }
@@ -74,13 +76,13 @@ export interface ResolvedUserIdentity {
74
76
  handle: string;
75
77
  cohort: string;
76
78
  brainVersion: string;
77
- model: "rover";
78
- preset: PilotPreset;
79
+ bundles: CanonicalBundleId[];
80
+ add: string[];
81
+ remove: string[];
79
82
  domain: string;
80
83
  cloudflareZoneId?: string | undefined;
81
84
  contentRepo: string;
82
85
  profileKind?: string | undefined;
83
- addOverride?: string[];
84
86
  siteOverride?: ResolvedSiteOverride;
85
87
  discordEnabled: boolean;
86
88
  discordAnchorUserId?: string;
@@ -0,0 +1,9 @@
1
+ export interface ReconcileDryRunResult {
2
+ firstPassChangedFiles: string[];
3
+ secondPassChangedFiles: string[];
4
+ }
5
+ /**
6
+ * Reconcile an isolated copy twice with external content-repo access disabled.
7
+ * The source repository is never written.
8
+ */
9
+ export declare function dryRunReconcileAll(rootDir: string): Promise<ReconcileDryRunResult>;
package/dist/schema.d.ts CHANGED
@@ -1,26 +1,46 @@
1
1
  import { z } from "@brains/utils/zod";
2
- export declare const presetSchema: z.ZodEnum<{
3
- core: "core";
4
- default: "default";
5
- pro: "pro";
6
- }>;
7
2
  export declare const exactVersionSchema: z.ZodString;
8
3
  export declare const handleSchema: z.ZodString;
9
4
  export declare const secretNameSchema: z.ZodString;
10
5
  export declare const agePublicKeySchema: z.ZodString;
11
- export declare const pilotSchema: z.ZodObject<{
12
- schemaVersion: z.ZodLiteral<1>;
13
- brainVersion: z.ZodString;
14
- model: z.ZodLiteral<"rover">;
15
- githubOrg: z.ZodString;
16
- contentRepoPrefix: z.ZodString;
17
- domainSuffix: z.ZodString;
18
- preset: typeof presetSchema;
19
- aiApiKey: z.ZodString;
20
- gitSyncToken: z.ZodString;
21
- contentRepoAdminToken: z.ZodString;
22
- agePublicKey: z.ZodString;
6
+ export declare const canonicalBundleIdSchema: z.ZodEnum<{
7
+ core: "core";
8
+ site: "site";
9
+ publishing: "publishing";
10
+ team: "team";
23
11
  }>;
12
+ export type CanonicalBundleId = z.output<typeof canonicalBundleIdSchema>;
13
+ export interface PilotConfig {
14
+ brainVersion: string;
15
+ bundles: CanonicalBundleId[];
16
+ add?: string[] | undefined;
17
+ remove?: string[] | undefined;
18
+ githubOrg: string;
19
+ contentRepoPrefix: string;
20
+ domainSuffix: string;
21
+ aiApiKey: string;
22
+ gitSyncToken: string;
23
+ contentRepoAdminToken: string;
24
+ agePublicKey: string;
25
+ }
26
+ export interface SiteOverrideConfig {
27
+ package: string;
28
+ version: string;
29
+ theme?: string | undefined;
30
+ themeVersion?: string | undefined;
31
+ }
32
+ export interface CohortConfig {
33
+ members: string[];
34
+ brainVersionOverride?: string | undefined;
35
+ bundlesOverride?: CanonicalBundleId[] | undefined;
36
+ addOverride?: string[] | undefined;
37
+ removeOverride?: string[] | undefined;
38
+ aiApiKeyOverride?: string | undefined;
39
+ gitSyncTokenOverride?: string | undefined;
40
+ }
41
+ /** Sole active desired-state contract for the canonical brain. */
42
+ export declare const pilotSchema: z.ZodType<PilotConfig>;
43
+ export declare const cohortSchema: z.ZodType<CohortConfig>;
24
44
  declare const anchorProfileSocialLinkSchema: z.ZodObject<{
25
45
  platform: z.ZodEnum<{
26
46
  github: "github";
@@ -70,12 +90,7 @@ declare const atprotoSchema: z.ZodObject<{
70
90
  lexiconAuthority: z.ZodOptional<z.ZodBoolean>;
71
91
  jetstream: z.ZodOptional<typeof atprotoJetstreamSchema>;
72
92
  }>;
73
- declare const siteOverrideSchema: z.ZodObject<{
74
- package: z.ZodString;
75
- version: z.ZodOptional<typeof exactVersionSchema>;
76
- theme: z.ZodOptional<z.ZodString>;
77
- themeVersion: z.ZodOptional<typeof exactVersionSchema>;
78
- }>;
93
+ export declare const siteOverrideSchema: z.ZodType<SiteOverrideConfig>;
79
94
  declare const playbooksSchema: z.ZodObject<{
80
95
  onboarding: z.ZodOptional<z.ZodBoolean>;
81
96
  }>;
@@ -92,24 +107,15 @@ export declare const userSchema: z.ZodObject<{
92
107
  contentRepoOverride: z.ZodOptional<z.ZodString>;
93
108
  profileKind: z.ZodOptional<z.ZodString>;
94
109
  addOverride: z.ZodOptional<z.ZodArray<z.ZodString>>;
110
+ removeOverride: z.ZodOptional<z.ZodArray<z.ZodString>>;
95
111
  siteOverride: z.ZodOptional<typeof siteOverrideSchema>;
96
112
  setup: z.ZodOptional<typeof setupDeliverySchema>;
97
113
  atproto: z.ZodOptional<typeof atprotoSchema>;
98
114
  playbooks: z.ZodOptional<typeof playbooksSchema>;
99
115
  anchorProfile: z.ZodOptional<typeof anchorProfileSchema>;
100
116
  }>;
101
- export declare const cohortSchema: z.ZodObject<{
102
- members: z.ZodArray<z.ZodString>;
103
- brainVersionOverride: z.ZodOptional<z.ZodString>;
104
- presetOverride: z.ZodOptional<typeof presetSchema>;
105
- aiApiKeyOverride: z.ZodOptional<z.ZodString>;
106
- gitSyncTokenOverride: z.ZodOptional<z.ZodString>;
107
- }>;
108
- export type PilotConfig = z.output<typeof pilotSchema>;
109
117
  export type PilotConfigInput = z.input<typeof pilotSchema>;
110
118
  export type UserConfig = z.output<typeof userSchema>;
111
119
  export type UserConfigInput = z.input<typeof userSchema>;
112
- export type CohortConfig = z.output<typeof cohortSchema>;
113
120
  export type CohortConfigInput = z.input<typeof cohortSchema>;
114
- export type PilotPreset = z.output<typeof presetSchema>;
115
121
  export {};
@@ -2,6 +2,8 @@ import { z } from "@brains/utils/zod";
2
2
  declare const encryptedUserSecretsSchema: z.ZodObject<{
3
3
  gitSyncToken: z.ZodOptional<z.ZodString>;
4
4
  discordBotToken: z.ZodOptional<z.ZodString>;
5
+ discordPublicKey: z.ZodOptional<z.ZodString>;
6
+ discordApplicationId: z.ZodOptional<z.ZodString>;
5
7
  aiApiKey: z.ZodOptional<z.ZodString>;
6
8
  atprotoAppPassword: z.ZodOptional<z.ZodString>;
7
9
  certificatePem: z.ZodOptional<z.ZodString>;
@@ -0,0 +1,20 @@
1
+ import { type SiteOverrideConfig } from "./schema";
2
+ export interface StagedCrossover {
3
+ outputDir: string;
4
+ changedFiles: string[];
5
+ }
6
+ interface ReviewedSitePinManifest {
7
+ sites: Record<string, SiteOverrideConfig>;
8
+ }
9
+ export type ReviewedSitePins = ReviewedSitePinManifest["sites"];
10
+ export interface StageLegacyCrossoverOptions {
11
+ sitePins?: ReviewedSitePins | undefined;
12
+ }
13
+ export declare function parseReviewedSitePins(input: string): ReviewedSitePins;
14
+ export declare function loadReviewedSitePins(filePath: string): Promise<ReviewedSitePins>;
15
+ /**
16
+ * Build a complete review copy without mutating the source repository.
17
+ * This is offline migration tooling; active desired-state loading is canonical-only.
18
+ */
19
+ export declare function stageLegacyCrossover(sourceDir: string, outputDir: string, options?: StageLegacyCrossoverOptions): Promise<StagedCrossover>;
20
+ export {};
@@ -10,7 +10,7 @@ export interface FailedCheck {
10
10
  }
11
11
  export interface VerifyPilotUserResult {
12
12
  handle: string;
13
- preset: ResolvedUser["preset"];
13
+ bundles: ResolvedUser["bundles"];
14
14
  domain: string;
15
15
  contentRepo: string;
16
16
  checks: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rizom/ops",
3
- "version": "0.2.0-alpha.243",
3
+ "version": "0.2.0-alpha.245",
4
4
  "description": "Operator CLI for managing private brain fleet registry repos",
5
5
  "keywords": [
6
6
  "brains",
@@ -17,7 +17,8 @@
17
17
  },
18
18
  "bugs": "https://github.com/rizom-ai/brains/issues",
19
19
  "dependencies": {
20
- "age-encryption": "^0.3.0"
20
+ "age-encryption": "^0.3.0",
21
+ "yaml": "^2.9.0"
21
22
  },
22
23
  "devDependencies": {
23
24
  "@brains/deploy-support": "workspace:*",
@@ -25,6 +25,13 @@ CONTENT_REPO_ADMIN_TOKEN=
25
25
  # @sensitive
26
26
  DISCORD_BOT_TOKEN=
27
27
 
28
+ # Stored with the per-user Discord credentials and injected at deploy time.
29
+ # @sensitive
30
+ DISCORD_PUBLIC_KEY=
31
+
32
+ # @sensitive
33
+ DISCORD_APPLICATION_ID=
34
+
28
35
  # AT Protocol publishing/discovery (optional, per-user)
29
36
  # Comes from the decrypted users/<handle>.secrets.yaml.age file when configured.
30
37
  # @sensitive
@@ -39,23 +39,23 @@ jobs:
39
39
  # `git diff` cannot see untracked files — without intent-to-add
40
40
  # the new config is silently dropped here and every later deploy
41
41
  # skips that user (their files never appear in a commit range).
42
- git add --intent-to-add -- views users
42
+ git add --intent-to-add -- users
43
43
 
44
- if git diff --quiet -- views users; then
44
+ if git diff --quiet -- users; then
45
45
  exit 0
46
46
  fi
47
47
 
48
48
  git fetch origin "${{ github.ref_name }}"
49
- if git diff --quiet "origin/${{ github.ref_name }}" -- views users; then
49
+ if git diff --quiet "origin/${{ github.ref_name }}" -- users; then
50
50
  exit 0
51
51
  fi
52
52
 
53
53
  patch_file="$(mktemp)"
54
- git diff --binary -- views users > "$patch_file"
54
+ git diff --binary -- users > "$patch_file"
55
55
  git reset --hard "origin/${{ github.ref_name }}"
56
56
  git apply --3way --index "$patch_file"
57
57
 
58
- if git diff --cached --quiet -- views users; then
58
+ if git diff --cached --quiet -- users; then
59
59
  exit 0
60
60
  fi
61
61
 
@@ -1,6 +1,6 @@
1
- # rover-pilot
1
+ # private brain pilot
2
2
 
3
- Private desired-state repo for the rover pilot.
3
+ Private desired-state repository for the hosted brain pilot. The generated repository/service name may remain `rover-pilot` for deployment identity compatibility.
4
4
 
5
5
  This is a single operator-owned repo. Pilot users do not get their own brain repos.
6
6
  Per-user deploy config lives under `users/<handle>/`, while content stays in per-user content repos.
@@ -31,7 +31,7 @@ The repo also checks in its deploy contract:
31
31
  `.env.schema` is the single source of truth for required and sensitive deploy vars.
32
32
  Use separate GitHub tokens: `CONTENT_REPO_ADMIN_TOKEN` for operator-side content repo creation/checks, and `GIT_SYNC_TOKEN` for runtime directory-sync git access.
33
33
  The default pilot image tag is `brain-${brainVersion}` end to end. A user with `siteOverride` gets an isolated `brain-${brainVersion}-sites-${packageHash}` image instead.
34
- When the effective brain version (`pilot.yaml.brainVersion`, or a cohort override) changes and you push, CI rebuilds the required default/site tags, refreshes generated user env files, and redeploys affected users. An omitted `siteOverride.version` follows that effective version; an explicit exact version remains pinned.
34
+ When the effective brain version (`pilot.yaml.brainVersion`, or a cohort override) changes and you push, CI rebuilds the required default/site tags, refreshes generated user env files, and redeploys affected users. Every external site and theme package uses its own required exact version pin; ops never infers either version from the brain or from another package.
35
35
  When a push changes only deploy contract files, CI prints `No affected user configs; skipping deploy.` and stops before Kamal.
36
36
 
37
37
  ## Commands
@@ -46,3 +46,8 @@ When a push changes only deploy contract files, CI prints `No affected user conf
46
46
  - `brains-ops secrets:encrypt <repo> <handle>`
47
47
  - `brains-ops reconcile-cohort <repo> <cohort>`
48
48
  - `brains-ops reconcile-all <repo>`
49
+ - `brains-ops reconcile-all <repo> --dry-run` (isolated, no external content-repository access; lists both passes' changed files)
50
+
51
+ `render` owns the observational `views/users.md` projection. Reconcile owns generated per-user config and never rewrites observed status rows.
52
+
53
+ Use `docs/canonical-crossover-record.md` to record exact forward and rollback artifact pins before an approved crossover window.
@@ -25,6 +25,8 @@ writeSecretGitHubEnv(
25
25
  secrets["cmsContentRepoPat"] ?? secrets["gitSyncToken"],
26
26
  );
27
27
  writeSecretGitHubEnv("DISCORD_BOT_TOKEN", secrets["discordBotToken"]);
28
+ writeSecretGitHubEnv("DISCORD_PUBLIC_KEY", secrets["discordPublicKey"]);
29
+ writeSecretGitHubEnv("DISCORD_APPLICATION_ID", secrets["discordApplicationId"]);
28
30
  // Per-user AT Protocol publishing credential (optional; from the user's
29
31
  // encrypted secrets file). The scaffold wires this so a pilot can publish its
30
32
  // brain's agent card to its PDS; a deployment that doesn't publish simply
@@ -14,6 +14,17 @@ for (const key of requiredKeys) {
14
14
  }
15
15
  }
16
16
 
17
+ const discordKeys = [
18
+ "DISCORD_BOT_TOKEN",
19
+ "DISCORD_PUBLIC_KEY",
20
+ "DISCORD_APPLICATION_ID",
21
+ ];
22
+ if (discordKeys.some((key) => process.env[key])) {
23
+ for (const key of discordKeys) {
24
+ if (!process.env[key]) missing.push(key);
25
+ }
26
+ }
27
+
17
28
  if (missing.length > 0) {
18
29
  throw new Error(`Missing required secrets: ${missing.join(", ")}`);
19
30
  }
@@ -0,0 +1,104 @@
1
+ # Canonical Crossover Approval Record
2
+
3
+ > Draft evidence record only. Completing this file does not authorize a merge, publish, reconcile, or deployment.
4
+
5
+ Do not include secret values, private keys, access tokens, or decrypted user configuration.
6
+
7
+ ## Authorization and freeze
8
+
9
+ - Operator:
10
+ - Approval reference:
11
+ - Approved window:
12
+ - Freeze start:
13
+ - Build workflow disabled and idle:
14
+ - Reconcile workflow disabled and idle:
15
+ - Deploy workflow disabled and idle:
16
+
17
+ ## Reviewed inputs
18
+
19
+ - `brains` crossover commit:
20
+ - Private-pilot source commit used for staging:
21
+ - Canonical review commit:
22
+ - Secret-free review diff SHA-256:
23
+ - Identity-review evidence SHA-256:
24
+ - Reviewed hosted-site pin manifest SHA-256:
25
+ - Source and review worktrees clean:
26
+
27
+ ## Hosted site and theme pins
28
+
29
+ List every hosted site. External site and theme package versions must be exact and must
30
+ match the staged user desired state; do not infer them from the brain or from each other.
31
+
32
+ | Handle | Site package | Exact site version | Theme package | Exact theme version | Package/image evidence |
33
+ | ------ | ------------ | ------------------ | ------------- | ------------------- | ---------------------- |
34
+ | | | | | | |
35
+
36
+ ## Forward artifact pins
37
+
38
+ | Artifact | Exact version | Registry integrity or digest | Verified installable |
39
+ | ---------------------- | ------------- | ---------------------------- | -------------------- |
40
+ | `@rizom/brain` | | | |
41
+ | `@rizom/ops` | | | |
42
+ | private-pilot lockfile | n/a | | |
43
+
44
+ Record every image. Tags alone are not immutable evidence.
45
+
46
+ | Cohort/handle | Config commit | Image repository | Image tag | Image digest |
47
+ | ------------- | ------------- | ---------------- | --------- | ------------ |
48
+ | | | | | |
49
+
50
+ ## Rollback pair
51
+
52
+ - Prior private-pilot commit:
53
+ - Prior `@rizom/ops` version and registry integrity:
54
+
55
+ | Cohort/handle | Prior config commit | Prior image repository | Prior image tag | Prior image digest |
56
+ | ------------- | ------------------- | ---------------------- | --------------- | ------------------ |
57
+ | | | | | |
58
+
59
+ ## Identity review
60
+
61
+ - Repository and image names unchanged or explicitly approved:
62
+ - GitHub organization and content repository identities unchanged:
63
+ - Server, domain, Cloudflare zone, and ATProto identities unchanged:
64
+ - Secret selector names and encrypted secret artifacts unchanged:
65
+ - Per-user runtime-version and site-package tag inputs reviewed:
66
+ - No plaintext source secrets copied into the review artifact:
67
+ - No removed model, preset, or old-format schema discriminator remains:
68
+ - Every external hosted site and theme package has an exact reviewed pin:
69
+
70
+ ## Offline convergence
71
+
72
+ Command:
73
+
74
+ ```sh
75
+ bunx brains-ops reconcile-all <canonical-review-copy> --dry-run
76
+ ```
77
+
78
+ - First-pass reconciler-owned changed files:
79
+ - Second-pass reconciler-owned changed files (must be zero):
80
+ - Observational `views/users.md` unchanged by reconciliation:
81
+ - Review copy unchanged:
82
+ - External content-repository access blocked:
83
+
84
+ ## Pre-window validation
85
+
86
+ - Package tests, typecheck, lint, build, and packed-consumer startup:
87
+ - Architecture and dependency boundaries:
88
+ - Environment-schema and workspace checks:
89
+ - Crossover migration and comment preservation:
90
+ - Canary-first order and health checks reviewed:
91
+ - Paired rollback reviewed:
92
+
93
+ ## Execution record
94
+
95
+ Complete only inside the explicitly approved maintenance window.
96
+
97
+ - Unified packages published and verified:
98
+ - Canonical desired state committed while automation remained frozen:
99
+ - Image digest set matched this record:
100
+ - First reconcile reviewed:
101
+ - Per-instance health, MCP authorization, identity, content, and site checks:
102
+ - Second reconcile produced zero reconciler-owned drift and no deploy work:
103
+ - Freeze lifted:
104
+ - One-week soak start:
@@ -23,18 +23,14 @@
23
23
  10. Run `bunx brains-ops secrets:encrypt <repo> <handle>`.
24
24
  11. Commit and push `users/<handle>.secrets.yaml.age`.
25
25
  12. Run `bunx brains-ops onboard <repo> <handle>`.
26
- 13. Verify the deployed Rover contract:
27
- - all presets:
28
- - `https://<handle>.rizom.ai/health` returns `200`
29
- - `https://<handle>.rizom.ai/chat` loads the web chat and accepts passkey sign-in
30
- - `https://<handle>.rizom.ai/` loads the dashboard (or site surface on `default` preset)
31
- - `https://<handle>.rizom.ai/cms` loads the CMS/login surface
32
- - unauthenticated `POST https://<handle>.rizom.ai/mcp` returns the expected auth failure
33
- - content repo exists and runtime sync is healthy
34
- - background jobs are not repeatedly failing, except for expected missing optional integrations
35
- - for `presetOverride: default` users:
36
- - initial site build completes
37
- 14. For fleet upgrades, edit `pilot.yaml.brainVersion` and push once; CI rebuilds the required default/site image tags, refreshes generated user env files, and redeploys affected users. Site overrides follow the user's effective brain version unless they declare an explicit exact `version` pin.
26
+ 13. Verify the deployed canonical contract:
27
+ - `https://<handle>.rizom.ai/health` returns `200`
28
+ - `https://<handle>.rizom.ai/chat` loads the web chat and accepts passkey sign-in
29
+ - unauthenticated `POST https://<handle>.rizom.ai/mcp` returns the expected auth failure
30
+ - content repo exists and runtime sync is healthy
31
+ - background jobs are not repeatedly failing, except for expected missing optional integrations
32
+ - when `site` is selected, the browser/CMS surfaces load and the initial app-managed site build completes
33
+ 14. For fleet upgrades, edit `pilot.yaml.brainVersion` and push once; CI rebuilds the required default/site image tags, refreshes generated user env files, and redeploys affected users. Every external site and theme package keeps its own required exact version pin and never follows the brain version implicitly.
38
34
  15. Confirm the user received the setup email, registered their passkey, and can sign in to web chat at `https://<handle>.rizom.ai/chat`. That completes the default onboarding; everything below is per-cohort extras.
39
35
  16. Hand over the browser surfaces:
40
36
  - Chat (primary): `https://<handle>.rizom.ai/chat`