@rizom/ops 0.2.0-alpha.8 → 0.2.0-alpha.80

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 (51) hide show
  1. package/README.md +7 -3
  2. package/dist/age-key-bootstrap.d.ts +17 -0
  3. package/dist/brains-ops.js +278 -156
  4. package/dist/cert-bootstrap.d.ts +3 -3
  5. package/dist/content-repo.d.ts +13 -0
  6. package/dist/default-user-runner.d.ts +1 -1
  7. package/dist/deploy.js +3 -170
  8. package/dist/entries/deploy.d.ts +2 -2
  9. package/dist/index.d.ts +4 -0
  10. package/dist/index.js +278 -156
  11. package/dist/load-registry.d.ts +22 -3
  12. package/dist/observed-status.d.ts +1 -1
  13. package/dist/onboard-user.d.ts +2 -2
  14. package/dist/origin-ca.d.ts +1 -1
  15. package/dist/parse-args.d.ts +2 -0
  16. package/dist/push-secrets.d.ts +1 -1
  17. package/dist/reconcile-all.d.ts +2 -2
  18. package/dist/reconcile-cohort.d.ts +2 -2
  19. package/dist/reconcile-lib.d.ts +4 -2
  20. package/dist/run-command.d.ts +1 -2
  21. package/dist/run-subprocess.d.ts +1 -0
  22. package/dist/schema.d.ts +107 -0
  23. package/dist/secrets-encrypt.d.ts +29 -0
  24. package/dist/secrets-push.d.ts +1 -1
  25. package/dist/ssh-key-bootstrap.d.ts +1 -1
  26. package/dist/user-add.d.ts +15 -0
  27. package/dist/user-runner.d.ts +5 -0
  28. package/dist/verify-user.d.ts +19 -0
  29. package/package.json +7 -3
  30. package/templates/rover-pilot/.env.schema +16 -2
  31. package/templates/rover-pilot/.github/workflows/build.yml +13 -5
  32. package/templates/rover-pilot/.github/workflows/deploy.yml +73 -20
  33. package/templates/rover-pilot/.github/workflows/reconcile.yml +16 -2
  34. package/templates/rover-pilot/README.md +6 -3
  35. package/templates/rover-pilot/deploy/scripts/decrypt-user-secrets.ts +78 -0
  36. package/templates/rover-pilot/deploy/scripts/provision-server.ts +1 -1
  37. package/templates/rover-pilot/deploy/scripts/resolve-deploy-handles.ts +15 -4
  38. package/templates/rover-pilot/deploy/scripts/resolve-user-config.ts +12 -12
  39. package/templates/rover-pilot/deploy/scripts/sync-content-repo.ts +179 -0
  40. package/templates/rover-pilot/deploy/scripts/update-dns.ts +14 -4
  41. package/templates/rover-pilot/docs/onboarding-checklist.md +40 -14
  42. package/templates/rover-pilot/docs/operator-playbook.md +129 -10
  43. package/templates/rover-pilot/docs/user-onboarding.md +182 -199
  44. package/templates/rover-pilot/package.json +3 -0
  45. package/templates/rover-pilot/pilot.yaml +3 -0
  46. package/templates/rover-pilot/users/alice.yaml +5 -1
  47. package/dist/user-secret-names.d.ts +0 -6
  48. package/templates/rover-pilot/.kamal/hooks/pre-deploy +0 -9
  49. package/templates/rover-pilot/deploy/Caddyfile +0 -66
  50. package/templates/rover-pilot/deploy/Dockerfile +0 -38
  51. package/templates/rover-pilot/deploy/kamal/deploy.yml +0 -40
@@ -13,6 +13,24 @@ export interface ResolvedCohort {
13
13
  brainVersionOverride?: string;
14
14
  presetOverride?: PilotPreset;
15
15
  aiApiKeyOverride?: string;
16
+ gitSyncTokenOverride?: string;
17
+ }
18
+ export interface ResolvedAnchorProfileSocialLink {
19
+ platform: "github" | "instagram" | "linkedin" | "email" | "website";
20
+ url: string;
21
+ label?: string;
22
+ }
23
+ export interface ResolvedAnchorProfile {
24
+ name: string;
25
+ description?: string;
26
+ website?: string;
27
+ email?: string;
28
+ story?: string;
29
+ socialLinks?: ResolvedAnchorProfileSocialLink[];
30
+ }
31
+ export interface ResolvedSetupDelivery {
32
+ delivery: "email";
33
+ email: string;
16
34
  }
17
35
  export interface ResolvedUserIdentity {
18
36
  handle: string;
@@ -23,7 +41,11 @@ export interface ResolvedUserIdentity {
23
41
  domain: string;
24
42
  contentRepo: string;
25
43
  discordEnabled: boolean;
44
+ discordAnchorUserId?: string;
26
45
  effectiveAiApiKey: string;
46
+ effectiveGitSyncToken: string;
47
+ setup?: ResolvedSetupDelivery;
48
+ anchorProfile: ResolvedAnchorProfile;
27
49
  snapshotStatus: SnapshotStatus;
28
50
  }
29
51
  export interface ResolvedUser extends ResolvedUserIdentity {
@@ -40,7 +62,4 @@ export interface PilotRegistry {
40
62
  cohorts: ResolvedCohort[];
41
63
  users: ResolvedUser[];
42
64
  }
43
- declare class PilotRegistryError extends Error {
44
- }
45
65
  export declare function loadPilotRegistry(rootDir: string, options?: LoadPilotRegistryOptions): Promise<PilotRegistry>;
46
- export { PilotRegistryError };
@@ -1,4 +1,4 @@
1
- import type { FetchLike } from "@brains/utils/origin-ca";
1
+ import type { FetchLike } from "@brains/deploy-support/origin-ca";
2
2
  import type { ObservedUserStatus, ResolvedUserIdentity } from "./load-registry";
3
3
  export interface LookupResult {
4
4
  address: string;
@@ -1,2 +1,2 @@
1
- import { type UserRunner } from "./reconcile-lib";
2
- export declare function onboardUser(rootDir: string, handle: string, runner?: UserRunner): Promise<void>;
1
+ import { type UserRunner, type ContentRepoSyncOptions } from "./reconcile-lib";
2
+ export declare function onboardUser(rootDir: string, handle: string, runner?: UserRunner, contentRepoOptions?: ContentRepoSyncOptions): Promise<void>;
@@ -1 +1 @@
1
- export { createOriginCertificateRequest, generateOriginKeyPair, issueCloudflareOriginCertificate, setCloudflareZoneSslStrict, type CloudflareOriginCaResult, type FetchLike, type OriginCertificateRequest, type OriginKeyPair, } from "@brains/utils/origin-ca";
1
+ export { createOriginCertificateRequest, generateOriginKeyPair, issueCloudflareOriginCertificate, setCloudflareZoneSslStrict, type CloudflareOriginCaResult, type FetchLike, type OriginCertificateRequest, type OriginKeyPair, } from "@brains/deploy-support/origin-ca";
@@ -6,6 +6,8 @@ export interface ParsedArgs {
6
6
  version?: boolean | undefined;
7
7
  dryRun?: boolean | undefined;
8
8
  pushTo?: string | undefined;
9
+ cohort?: string | undefined;
10
+ anchorId?: string | undefined;
9
11
  };
10
12
  }
11
13
  export declare function parseArgs(argv: string[]): ParsedArgs;
@@ -5,5 +5,5 @@ export interface PushSecretsOptions {
5
5
  runCommand?: RunCommand | undefined;
6
6
  logger?: ((message: string) => void) | undefined;
7
7
  }
8
- export declare function pushSecretsToBackend(target: PushTarget, secrets: readonly SecretPair[], options?: PushSecretsOptions): Promise<void>;
8
+ export declare function pushSecretsToBackend(_target: PushTarget, secrets: readonly SecretPair[], options?: PushSecretsOptions): Promise<void>;
9
9
  export { normalizePushTarget };
@@ -1,2 +1,2 @@
1
- import { type UserRunner } from "./reconcile-lib";
2
- export declare function reconcileAll(rootDir: string, runner?: UserRunner): Promise<void>;
1
+ import { type UserRunner, type ContentRepoSyncOptions } from "./reconcile-lib";
2
+ export declare function reconcileAll(rootDir: string, runner?: UserRunner, contentRepoOptions?: ContentRepoSyncOptions): Promise<void>;
@@ -1,2 +1,2 @@
1
- import { type UserRunner } from "./reconcile-lib";
2
- export declare function reconcileCohort(rootDir: string, cohortId: string, runner?: UserRunner): Promise<void>;
1
+ import { type UserRunner, type ContentRepoSyncOptions } from "./reconcile-lib";
2
+ export declare function reconcileCohort(rootDir: string, cohortId: string, runner?: UserRunner, contentRepoOptions?: ContentRepoSyncOptions): Promise<void>;
@@ -1,7 +1,9 @@
1
+ import { type ContentRepoSyncOptions } from "./content-repo";
1
2
  import { type PilotRegistry, type ResolvedUser } from "./load-registry";
2
3
  import type { UserRunner } from "./user-runner";
3
- export type { UserRunResult, UserRunner } from "./user-runner";
4
- export declare function runUsers(rootDir: string, registry: PilotRegistry, users: ResolvedUser[], runner?: UserRunner): Promise<void>;
4
+ export type { ContentRepoSyncOptions } from "./content-repo";
5
+ export type { ContentRepoFile, UserRunResult, UserRunner } from "./user-runner";
6
+ export declare function runUsers(rootDir: string, registry: PilotRegistry, users: ResolvedUser[], runner?: UserRunner, contentRepoOptions?: ContentRepoSyncOptions): Promise<void>;
5
7
  export declare function findUser(rootDir: string, handle: string): Promise<{
6
8
  registry: PilotRegistry;
7
9
  user: ResolvedUser;
@@ -1,4 +1,4 @@
1
- import type { FetchLike } from "@brains/utils/origin-ca";
1
+ import type { FetchLike } from "@brains/deploy-support/origin-ca";
2
2
  import type { LoadPilotRegistryOptions } from "./load-registry";
3
3
  import { type LookupHost } from "./observed-status";
4
4
  import type { ParsedArgs } from "./parse-args";
@@ -15,7 +15,6 @@ export interface CommandDependencies extends LoadPilotRegistryOptions {
15
15
  logger?: ((message: string) => void) | undefined;
16
16
  fetchImpl?: FetchLike | undefined;
17
17
  lookupHost?: LookupHost | undefined;
18
- secretRunCommand?: OpsRunCommand | undefined;
19
18
  bootstrapRunCommand?: OpsRunCommand | undefined;
20
19
  sshKeygen?: SshKeygen | undefined;
21
20
  }
@@ -1,5 +1,6 @@
1
1
  export type RunCommand = (command: string, args: string[], options?: {
2
2
  stdin?: string;
3
3
  env?: NodeJS.ProcessEnv;
4
+ cwd?: string;
4
5
  }) => Promise<void>;
5
6
  export declare const runSubprocess: RunCommand;
package/dist/schema.d.ts CHANGED
@@ -3,6 +3,7 @@ export declare const presetSchema: z.ZodEnum<["core", "default", "pro"]>;
3
3
  export declare const exactVersionSchema: z.ZodString;
4
4
  export declare const handleSchema: z.ZodString;
5
5
  export declare const secretNameSchema: z.ZodString;
6
+ export declare const agePublicKeySchema: z.ZodString;
6
7
  export declare const pilotSchema: z.ZodObject<{
7
8
  schemaVersion: z.ZodLiteral<1>;
8
9
  brainVersion: z.ZodString;
@@ -12,7 +13,11 @@ export declare const pilotSchema: z.ZodObject<{
12
13
  domainSuffix: z.ZodString;
13
14
  preset: z.ZodEnum<["core", "default", "pro"]>;
14
15
  aiApiKey: z.ZodString;
16
+ gitSyncToken: z.ZodString;
17
+ contentRepoAdminToken: z.ZodString;
18
+ agePublicKey: z.ZodString;
15
19
  }, "strict", z.ZodTypeAny, {
20
+ agePublicKey: string;
16
21
  schemaVersion: 1;
17
22
  brainVersion: string;
18
23
  model: "rover";
@@ -21,7 +26,10 @@ export declare const pilotSchema: z.ZodObject<{
21
26
  domainSuffix: string;
22
27
  preset: "default" | "core" | "pro";
23
28
  aiApiKey: string;
29
+ gitSyncToken: string;
30
+ contentRepoAdminToken: string;
24
31
  }, {
32
+ agePublicKey: string;
25
33
  schemaVersion: 1;
26
34
  brainVersion: string;
27
35
  model: "rover";
@@ -30,53 +38,152 @@ export declare const pilotSchema: z.ZodObject<{
30
38
  domainSuffix: string;
31
39
  preset: "default" | "core" | "pro";
32
40
  aiApiKey: string;
41
+ gitSyncToken: string;
42
+ contentRepoAdminToken: string;
33
43
  }>;
34
44
  export declare const userSchema: z.ZodObject<{
35
45
  handle: z.ZodString;
36
46
  discord: z.ZodObject<{
37
47
  enabled: z.ZodBoolean;
48
+ anchorUserId: z.ZodOptional<z.ZodString>;
38
49
  }, "strict", z.ZodTypeAny, {
39
50
  enabled: boolean;
51
+ anchorUserId?: string | undefined;
40
52
  }, {
41
53
  enabled: boolean;
54
+ anchorUserId?: string | undefined;
42
55
  }>;
43
56
  aiApiKeyOverride: z.ZodOptional<z.ZodString>;
57
+ gitSyncTokenOverride: z.ZodOptional<z.ZodString>;
58
+ setup: z.ZodOptional<z.ZodObject<{
59
+ delivery: z.ZodLiteral<"email">;
60
+ email: z.ZodString;
61
+ }, "strict", z.ZodTypeAny, {
62
+ email: string;
63
+ delivery: "email";
64
+ }, {
65
+ email: string;
66
+ delivery: "email";
67
+ }>>;
68
+ anchorProfile: z.ZodOptional<z.ZodObject<{
69
+ name: z.ZodOptional<z.ZodString>;
70
+ description: z.ZodOptional<z.ZodString>;
71
+ website: z.ZodOptional<z.ZodString>;
72
+ email: z.ZodOptional<z.ZodString>;
73
+ story: z.ZodOptional<z.ZodString>;
74
+ socialLinks: z.ZodOptional<z.ZodArray<z.ZodObject<{
75
+ platform: z.ZodEnum<["github", "instagram", "linkedin", "email", "website"]>;
76
+ url: z.ZodString;
77
+ label: z.ZodOptional<z.ZodString>;
78
+ }, "strict", z.ZodTypeAny, {
79
+ platform: "github" | "instagram" | "linkedin" | "email" | "website";
80
+ url: string;
81
+ label?: string | undefined;
82
+ }, {
83
+ platform: "github" | "instagram" | "linkedin" | "email" | "website";
84
+ url: string;
85
+ label?: string | undefined;
86
+ }>, "many">>;
87
+ }, "strict", z.ZodTypeAny, {
88
+ name?: string | undefined;
89
+ email?: string | undefined;
90
+ website?: string | undefined;
91
+ description?: string | undefined;
92
+ story?: string | undefined;
93
+ socialLinks?: {
94
+ platform: "github" | "instagram" | "linkedin" | "email" | "website";
95
+ url: string;
96
+ label?: string | undefined;
97
+ }[] | undefined;
98
+ }, {
99
+ name?: string | undefined;
100
+ email?: string | undefined;
101
+ website?: string | undefined;
102
+ description?: string | undefined;
103
+ story?: string | undefined;
104
+ socialLinks?: {
105
+ platform: "github" | "instagram" | "linkedin" | "email" | "website";
106
+ url: string;
107
+ label?: string | undefined;
108
+ }[] | undefined;
109
+ }>>;
44
110
  }, "strict", z.ZodTypeAny, {
45
111
  handle: string;
46
112
  discord: {
47
113
  enabled: boolean;
114
+ anchorUserId?: string | undefined;
48
115
  };
49
116
  aiApiKeyOverride?: string | undefined;
117
+ gitSyncTokenOverride?: string | undefined;
118
+ setup?: {
119
+ email: string;
120
+ delivery: "email";
121
+ } | undefined;
122
+ anchorProfile?: {
123
+ name?: string | undefined;
124
+ email?: string | undefined;
125
+ website?: string | undefined;
126
+ description?: string | undefined;
127
+ story?: string | undefined;
128
+ socialLinks?: {
129
+ platform: "github" | "instagram" | "linkedin" | "email" | "website";
130
+ url: string;
131
+ label?: string | undefined;
132
+ }[] | undefined;
133
+ } | undefined;
50
134
  }, {
51
135
  handle: string;
52
136
  discord: {
53
137
  enabled: boolean;
138
+ anchorUserId?: string | undefined;
54
139
  };
55
140
  aiApiKeyOverride?: string | undefined;
141
+ gitSyncTokenOverride?: string | undefined;
142
+ setup?: {
143
+ email: string;
144
+ delivery: "email";
145
+ } | undefined;
146
+ anchorProfile?: {
147
+ name?: string | undefined;
148
+ email?: string | undefined;
149
+ website?: string | undefined;
150
+ description?: string | undefined;
151
+ story?: string | undefined;
152
+ socialLinks?: {
153
+ platform: "github" | "instagram" | "linkedin" | "email" | "website";
154
+ url: string;
155
+ label?: string | undefined;
156
+ }[] | undefined;
157
+ } | undefined;
56
158
  }>;
57
159
  export declare const cohortSchema: z.ZodEffects<z.ZodObject<{
58
160
  members: z.ZodArray<z.ZodString, "many">;
59
161
  brainVersionOverride: z.ZodOptional<z.ZodString>;
60
162
  presetOverride: z.ZodOptional<z.ZodEnum<["core", "default", "pro"]>>;
61
163
  aiApiKeyOverride: z.ZodOptional<z.ZodString>;
164
+ gitSyncTokenOverride: z.ZodOptional<z.ZodString>;
62
165
  }, "strict", z.ZodTypeAny, {
63
166
  members: string[];
64
167
  aiApiKeyOverride?: string | undefined;
168
+ gitSyncTokenOverride?: string | undefined;
65
169
  brainVersionOverride?: string | undefined;
66
170
  presetOverride?: "default" | "core" | "pro" | undefined;
67
171
  }, {
68
172
  members: string[];
69
173
  aiApiKeyOverride?: string | undefined;
174
+ gitSyncTokenOverride?: string | undefined;
70
175
  brainVersionOverride?: string | undefined;
71
176
  presetOverride?: "default" | "core" | "pro" | undefined;
72
177
  }>, {
73
178
  members: string[];
74
179
  aiApiKeyOverride?: string | undefined;
180
+ gitSyncTokenOverride?: string | undefined;
75
181
  brainVersionOverride?: string | undefined;
76
182
  presetOverride?: "default" | "core" | "pro" | undefined;
77
183
  }, {
78
184
  members: string[];
79
185
  aiApiKeyOverride?: string | undefined;
186
+ gitSyncTokenOverride?: string | undefined;
80
187
  brainVersionOverride?: string | undefined;
81
188
  presetOverride?: "default" | "core" | "pro" | undefined;
82
189
  }>;
@@ -0,0 +1,29 @@
1
+ import { z } from "@brains/utils";
2
+ declare const encryptedUserSecretsSchema: z.ZodObject<{
3
+ gitSyncToken: z.ZodOptional<z.ZodString>;
4
+ discordBotToken: z.ZodOptional<z.ZodString>;
5
+ aiApiKey: z.ZodOptional<z.ZodString>;
6
+ }, "strict", z.ZodTypeAny, {
7
+ aiApiKey?: string | undefined;
8
+ gitSyncToken?: string | undefined;
9
+ discordBotToken?: string | undefined;
10
+ }, {
11
+ aiApiKey?: string | undefined;
12
+ gitSyncToken?: string | undefined;
13
+ discordBotToken?: string | undefined;
14
+ }>;
15
+ export type EncryptedUserSecrets = z.infer<typeof encryptedUserSecretsSchema>;
16
+ export interface SecretsEncryptOptions {
17
+ env?: NodeJS.ProcessEnv | undefined;
18
+ logger?: ((message: string) => void) | undefined;
19
+ dryRun?: boolean | undefined;
20
+ }
21
+ export interface SecretsEncryptResult {
22
+ encryptedPath: string;
23
+ plaintextPath: string;
24
+ deletedPlaintext: boolean;
25
+ encryptedKeys: Array<keyof EncryptedUserSecrets>;
26
+ dryRun?: boolean | undefined;
27
+ }
28
+ export declare function encryptPilotSecrets(rootDir: string, handle: string, options?: SecretsEncryptOptions): Promise<SecretsEncryptResult>;
29
+ export {};
@@ -10,4 +10,4 @@ export interface SecretsPushResult {
10
10
  skippedKeys: string[];
11
11
  dryRun?: boolean | undefined;
12
12
  }
13
- export declare function pushPilotSecrets(rootDir: string, handle: string, options?: SecretsPushOptions): Promise<SecretsPushResult>;
13
+ export declare function pushPilotSecrets(rootDir: string, options?: SecretsPushOptions): Promise<SecretsPushResult>;
@@ -1,4 +1,4 @@
1
- import { type FetchLike } from "@brains/utils/origin-ca";
1
+ import { type FetchLike } from "@brains/deploy-support/origin-ca";
2
2
  import { type RunCommand } from "./run-subprocess";
3
3
  export interface SshKeyBootstrapOptions {
4
4
  env?: NodeJS.ProcessEnv | undefined;
@@ -0,0 +1,15 @@
1
+ export interface AddPilotUserOptions {
2
+ cohort: string;
3
+ anchorId?: string | undefined;
4
+ }
5
+ export interface AddPilotUserResult {
6
+ handle: string;
7
+ cohort: string;
8
+ userPath: string;
9
+ secretsTemplatePath: string;
10
+ cohortPath: string;
11
+ createdUser: boolean;
12
+ createdSecretsTemplate: boolean;
13
+ addedToCohort: boolean;
14
+ }
15
+ export declare function addPilotUser(rootDir: string, handle: string, options: AddPilotUserOptions): Promise<AddPilotUserResult>;
@@ -1,6 +1,11 @@
1
1
  import type { ResolvedUser } from "./load-registry";
2
+ export interface ContentRepoFile {
3
+ path: string;
4
+ content: string;
5
+ }
2
6
  export interface UserRunResult {
3
7
  brainYaml?: string;
4
8
  envFile?: string;
9
+ contentRepoFiles?: ContentRepoFile[];
5
10
  }
6
11
  export type UserRunner = (user: ResolvedUser) => Promise<UserRunResult | void>;
@@ -0,0 +1,19 @@
1
+ import type { FetchLike } from "@brains/deploy-support/origin-ca";
2
+ import { type ResolvedUser } from "./load-registry";
3
+ export interface VerifyPilotUserOptions {
4
+ fetchImpl?: FetchLike;
5
+ logger?: (message: string) => void;
6
+ }
7
+ export interface FailedCheck {
8
+ name: string;
9
+ message: string;
10
+ }
11
+ export interface VerifyPilotUserResult {
12
+ handle: string;
13
+ preset: ResolvedUser["preset"];
14
+ domain: string;
15
+ contentRepo: string;
16
+ checks: string[];
17
+ failedChecks: FailedCheck[];
18
+ }
19
+ export declare function verifyPilotUser(rootDir: string, handle: string, options?: VerifyPilotUserOptions): Promise<VerifyPilotUserResult>;
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.2.0-alpha.8",
7
+ "version": "0.2.0-alpha.80",
8
8
  "type": "module",
9
9
  "exports": {
10
10
  ".": {
@@ -32,10 +32,14 @@
32
32
  "typecheck": "tsc --noEmit",
33
33
  "lint": "eslint . --ext .ts",
34
34
  "lint:fix": "eslint . --ext .ts --fix",
35
- "test": "bun test"
35
+ "test": "bun run build && bun test --timeout 20000",
36
+ "test:smoke": "bun run build && RUN_SMOKE_TESTS=1 bun test --timeout 60000"
37
+ },
38
+ "dependencies": {
39
+ "age-encryption": "^0.3.0"
36
40
  },
37
- "dependencies": {},
38
41
  "devDependencies": {
42
+ "@brains/deploy-support": "workspace:*",
39
43
  "@brains/eslint-config": "workspace:*",
40
44
  "@brains/typescript-config": "workspace:*",
41
45
  "@brains/utils": "workspace:*",
@@ -4,21 +4,35 @@
4
4
  # ----------
5
5
 
6
6
  # AI provider
7
+ # Shared GitHub secret by default; a per-user override may come from the decrypted
8
+ # users/<handle>.secrets.yaml.age file at deploy time.
7
9
  # @required @sensitive
8
10
  AI_API_KEY=
9
11
 
10
12
  # Git sync
13
+ # Comes from the decrypted users/<handle>.secrets.yaml.age file.
11
14
  # @required @sensitive
12
15
  GIT_SYNC_TOKEN=
13
16
 
14
- # MCP interface
17
+ # Content repo administration
18
+ # Local/operator secret only. Used by brains-ops to create missing GitHub repos;
19
+ # do not deploy it into Rover runtime config.
15
20
  # @required @sensitive
16
- MCP_AUTH_TOKEN=
21
+ CONTENT_REPO_ADMIN_TOKEN=
17
22
 
18
23
  # Discord (optional, per-user)
24
+ # Comes from the decrypted users/<handle>.secrets.yaml.age file when enabled.
19
25
  # @sensitive
20
26
  DISCORD_BOT_TOKEN=
21
27
 
28
+ # Setup email delivery (optional, shared)
29
+ # Used when a user file declares setup.delivery: email.
30
+ # @sensitive
31
+ SETUP_EMAIL_API_KEY=
32
+
33
+ # @sensitive
34
+ SETUP_EMAIL_FROM=
35
+
22
36
  # ---- deploy/provision vars ----
23
37
 
24
38
  # @required @sensitive
@@ -2,6 +2,11 @@ name: Build
2
2
 
3
3
  on:
4
4
  workflow_dispatch:
5
+ inputs:
6
+ brain_version:
7
+ description: Brain version to build; defaults to pilot.yaml brainVersion
8
+ required: false
9
+ type: string
5
10
  push:
6
11
  branches: ["main"]
7
12
  paths:
@@ -23,7 +28,10 @@ jobs:
23
28
 
24
29
  - name: Extract image metadata inputs
25
30
  run: |
26
- BRAIN_VERSION="$(grep '^brainVersion:' pilot.yaml | sed 's/^brainVersion:[[:space:]]*//' | tr -d '"' | tr -d "'")"
31
+ BRAIN_VERSION="${{ inputs.brain_version || '' }}"
32
+ if [ -z "$BRAIN_VERSION" ]; then
33
+ BRAIN_VERSION="$(grep '^brainVersion:' pilot.yaml | sed 's/^brainVersion:[[:space:]]*//' | tr -d '"' | tr -d "'")"
34
+ fi
27
35
  if [ -z "$BRAIN_VERSION" ]; then
28
36
  echo "Missing brainVersion in pilot.yaml" >&2
29
37
  exit 1
@@ -32,25 +40,25 @@ jobs:
32
40
  echo "IMAGE_REPOSITORY=ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}" >> "$GITHUB_ENV"
33
41
 
34
42
  - name: Set up Docker Buildx
35
- uses: docker/setup-buildx-action@v3
43
+ uses: docker/setup-buildx-action@v4
36
44
 
37
45
  - name: Extract image metadata
38
46
  id: meta
39
- uses: docker/metadata-action@v5
47
+ uses: docker/metadata-action@v6
40
48
  with:
41
49
  images: ${{ env.IMAGE_REPOSITORY }}
42
50
  tags: |
43
51
  type=raw,value=brain-${{ env.BRAIN_VERSION }}
44
52
 
45
53
  - name: Log in to GHCR
46
- uses: docker/login-action@v3
54
+ uses: docker/login-action@v4
47
55
  with:
48
56
  registry: ghcr.io
49
57
  username: ${{ github.actor }}
50
58
  password: ${{ secrets.GITHUB_TOKEN }}
51
59
 
52
60
  - name: Build and push image
53
- uses: docker/build-push-action@v6
61
+ uses: docker/build-push-action@v7
54
62
  with:
55
63
  context: .
56
64
  file: deploy/Dockerfile