@rizom/ops 0.2.0-alpha.71 → 0.2.0-alpha.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/brains-ops.js +90 -85
- package/dist/index.js +88 -83
- package/dist/load-registry.d.ts +5 -2
- package/dist/schema.d.ts +18 -11
- package/dist/secrets-encrypt.d.ts +0 -3
- package/package.json +1 -1
- package/templates/rover-pilot/.env.schema +8 -5
- package/templates/rover-pilot/.github/workflows/deploy.yml +4 -4
- package/templates/rover-pilot/deploy/scripts/decrypt-user-secrets.ts +0 -5
- package/templates/rover-pilot/docs/onboarding-checklist.md +5 -3
- package/templates/rover-pilot/docs/operator-playbook.md +64 -1
- package/templates/rover-pilot/pilot.yaml +0 -1
package/dist/load-registry.d.ts
CHANGED
|
@@ -14,7 +14,6 @@ export interface ResolvedCohort {
|
|
|
14
14
|
presetOverride?: PilotPreset;
|
|
15
15
|
aiApiKeyOverride?: string;
|
|
16
16
|
gitSyncTokenOverride?: string;
|
|
17
|
-
mcpAuthTokenOverride?: string;
|
|
18
17
|
}
|
|
19
18
|
export interface ResolvedAnchorProfileSocialLink {
|
|
20
19
|
platform: "github" | "instagram" | "linkedin" | "email" | "website";
|
|
@@ -29,6 +28,10 @@ export interface ResolvedAnchorProfile {
|
|
|
29
28
|
story?: string;
|
|
30
29
|
socialLinks?: ResolvedAnchorProfileSocialLink[];
|
|
31
30
|
}
|
|
31
|
+
export interface ResolvedSetupDelivery {
|
|
32
|
+
delivery: "email";
|
|
33
|
+
email: string;
|
|
34
|
+
}
|
|
32
35
|
export interface ResolvedUserIdentity {
|
|
33
36
|
handle: string;
|
|
34
37
|
cohort: string;
|
|
@@ -41,7 +44,7 @@ export interface ResolvedUserIdentity {
|
|
|
41
44
|
discordAnchorUserId?: string;
|
|
42
45
|
effectiveAiApiKey: string;
|
|
43
46
|
effectiveGitSyncToken: string;
|
|
44
|
-
|
|
47
|
+
setup?: ResolvedSetupDelivery;
|
|
45
48
|
anchorProfile: ResolvedAnchorProfile;
|
|
46
49
|
snapshotStatus: SnapshotStatus;
|
|
47
50
|
}
|
package/dist/schema.d.ts
CHANGED
|
@@ -15,7 +15,6 @@ export declare const pilotSchema: z.ZodObject<{
|
|
|
15
15
|
aiApiKey: z.ZodString;
|
|
16
16
|
gitSyncToken: z.ZodString;
|
|
17
17
|
contentRepoAdminToken: z.ZodString;
|
|
18
|
-
mcpAuthToken: z.ZodString;
|
|
19
18
|
agePublicKey: z.ZodString;
|
|
20
19
|
}, "strict", z.ZodTypeAny, {
|
|
21
20
|
agePublicKey: string;
|
|
@@ -29,7 +28,6 @@ export declare const pilotSchema: z.ZodObject<{
|
|
|
29
28
|
aiApiKey: string;
|
|
30
29
|
gitSyncToken: string;
|
|
31
30
|
contentRepoAdminToken: string;
|
|
32
|
-
mcpAuthToken: string;
|
|
33
31
|
}, {
|
|
34
32
|
agePublicKey: string;
|
|
35
33
|
schemaVersion: 1;
|
|
@@ -42,7 +40,6 @@ export declare const pilotSchema: z.ZodObject<{
|
|
|
42
40
|
aiApiKey: string;
|
|
43
41
|
gitSyncToken: string;
|
|
44
42
|
contentRepoAdminToken: string;
|
|
45
|
-
mcpAuthToken: string;
|
|
46
43
|
}>;
|
|
47
44
|
export declare const userSchema: z.ZodObject<{
|
|
48
45
|
handle: z.ZodString;
|
|
@@ -58,7 +55,16 @@ export declare const userSchema: z.ZodObject<{
|
|
|
58
55
|
}>;
|
|
59
56
|
aiApiKeyOverride: z.ZodOptional<z.ZodString>;
|
|
60
57
|
gitSyncTokenOverride: z.ZodOptional<z.ZodString>;
|
|
61
|
-
|
|
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
|
+
}>>;
|
|
62
68
|
anchorProfile: z.ZodOptional<z.ZodObject<{
|
|
63
69
|
name: z.ZodOptional<z.ZodString>;
|
|
64
70
|
description: z.ZodOptional<z.ZodString>;
|
|
@@ -109,7 +115,10 @@ export declare const userSchema: z.ZodObject<{
|
|
|
109
115
|
};
|
|
110
116
|
aiApiKeyOverride?: string | undefined;
|
|
111
117
|
gitSyncTokenOverride?: string | undefined;
|
|
112
|
-
|
|
118
|
+
setup?: {
|
|
119
|
+
email: string;
|
|
120
|
+
delivery: "email";
|
|
121
|
+
} | undefined;
|
|
113
122
|
anchorProfile?: {
|
|
114
123
|
name?: string | undefined;
|
|
115
124
|
email?: string | undefined;
|
|
@@ -130,7 +139,10 @@ export declare const userSchema: z.ZodObject<{
|
|
|
130
139
|
};
|
|
131
140
|
aiApiKeyOverride?: string | undefined;
|
|
132
141
|
gitSyncTokenOverride?: string | undefined;
|
|
133
|
-
|
|
142
|
+
setup?: {
|
|
143
|
+
email: string;
|
|
144
|
+
delivery: "email";
|
|
145
|
+
} | undefined;
|
|
134
146
|
anchorProfile?: {
|
|
135
147
|
name?: string | undefined;
|
|
136
148
|
email?: string | undefined;
|
|
@@ -150,33 +162,28 @@ export declare const cohortSchema: z.ZodEffects<z.ZodObject<{
|
|
|
150
162
|
presetOverride: z.ZodOptional<z.ZodEnum<["core", "default", "pro"]>>;
|
|
151
163
|
aiApiKeyOverride: z.ZodOptional<z.ZodString>;
|
|
152
164
|
gitSyncTokenOverride: z.ZodOptional<z.ZodString>;
|
|
153
|
-
mcpAuthTokenOverride: z.ZodOptional<z.ZodString>;
|
|
154
165
|
}, "strict", z.ZodTypeAny, {
|
|
155
166
|
members: string[];
|
|
156
167
|
aiApiKeyOverride?: string | undefined;
|
|
157
168
|
gitSyncTokenOverride?: string | undefined;
|
|
158
|
-
mcpAuthTokenOverride?: string | undefined;
|
|
159
169
|
brainVersionOverride?: string | undefined;
|
|
160
170
|
presetOverride?: "default" | "core" | "pro" | undefined;
|
|
161
171
|
}, {
|
|
162
172
|
members: string[];
|
|
163
173
|
aiApiKeyOverride?: string | undefined;
|
|
164
174
|
gitSyncTokenOverride?: string | undefined;
|
|
165
|
-
mcpAuthTokenOverride?: string | undefined;
|
|
166
175
|
brainVersionOverride?: string | undefined;
|
|
167
176
|
presetOverride?: "default" | "core" | "pro" | undefined;
|
|
168
177
|
}>, {
|
|
169
178
|
members: string[];
|
|
170
179
|
aiApiKeyOverride?: string | undefined;
|
|
171
180
|
gitSyncTokenOverride?: string | undefined;
|
|
172
|
-
mcpAuthTokenOverride?: string | undefined;
|
|
173
181
|
brainVersionOverride?: string | undefined;
|
|
174
182
|
presetOverride?: "default" | "core" | "pro" | undefined;
|
|
175
183
|
}, {
|
|
176
184
|
members: string[];
|
|
177
185
|
aiApiKeyOverride?: string | undefined;
|
|
178
186
|
gitSyncTokenOverride?: string | undefined;
|
|
179
|
-
mcpAuthTokenOverride?: string | undefined;
|
|
180
187
|
brainVersionOverride?: string | undefined;
|
|
181
188
|
presetOverride?: "default" | "core" | "pro" | undefined;
|
|
182
189
|
}>;
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
import { z } from "@brains/utils";
|
|
2
2
|
declare const encryptedUserSecretsSchema: z.ZodObject<{
|
|
3
3
|
gitSyncToken: z.ZodOptional<z.ZodString>;
|
|
4
|
-
mcpAuthToken: z.ZodOptional<z.ZodString>;
|
|
5
4
|
discordBotToken: z.ZodOptional<z.ZodString>;
|
|
6
5
|
aiApiKey: z.ZodOptional<z.ZodString>;
|
|
7
6
|
}, "strict", z.ZodTypeAny, {
|
|
8
7
|
aiApiKey?: string | undefined;
|
|
9
8
|
gitSyncToken?: string | undefined;
|
|
10
|
-
mcpAuthToken?: string | undefined;
|
|
11
9
|
discordBotToken?: string | undefined;
|
|
12
10
|
}, {
|
|
13
11
|
aiApiKey?: string | undefined;
|
|
14
12
|
gitSyncToken?: string | undefined;
|
|
15
|
-
mcpAuthToken?: string | undefined;
|
|
16
13
|
discordBotToken?: string | undefined;
|
|
17
14
|
}>;
|
|
18
15
|
export type EncryptedUserSecrets = z.infer<typeof encryptedUserSecretsSchema>;
|
package/package.json
CHANGED
|
@@ -20,16 +20,19 @@ GIT_SYNC_TOKEN=
|
|
|
20
20
|
# @required @sensitive
|
|
21
21
|
CONTENT_REPO_ADMIN_TOKEN=
|
|
22
22
|
|
|
23
|
-
# MCP interface
|
|
24
|
-
# Comes from the decrypted users/<handle>.secrets.yaml.age file.
|
|
25
|
-
# @required @sensitive
|
|
26
|
-
MCP_AUTH_TOKEN=
|
|
27
|
-
|
|
28
23
|
# Discord (optional, per-user)
|
|
29
24
|
# Comes from the decrypted users/<handle>.secrets.yaml.age file when enabled.
|
|
30
25
|
# @sensitive
|
|
31
26
|
DISCORD_BOT_TOKEN=
|
|
32
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
|
+
|
|
33
36
|
# ---- deploy/provision vars ----
|
|
34
37
|
|
|
35
38
|
# @required @sensitive
|
|
@@ -103,7 +103,8 @@ jobs:
|
|
|
103
103
|
env:
|
|
104
104
|
SHARED_AI_API_KEY: ${{ secrets[steps.user_secrets.outputs.shared_ai_api_key_secret_name] }}
|
|
105
105
|
SHARED_GIT_SYNC_TOKEN: ${{ secrets[steps.user_secrets.outputs.shared_git_sync_token_secret_name] }}
|
|
106
|
-
|
|
106
|
+
SETUP_EMAIL_API_KEY: ${{ secrets.SETUP_EMAIL_API_KEY }}
|
|
107
|
+
SETUP_EMAIL_FROM: ${{ secrets.SETUP_EMAIL_FROM }}
|
|
107
108
|
HCLOUD_TOKEN: ${{ secrets.HCLOUD_TOKEN }}
|
|
108
109
|
HCLOUD_SSH_KEY_NAME: ${{ secrets.HCLOUD_SSH_KEY_NAME }}
|
|
109
110
|
HCLOUD_SERVER_TYPE: ${{ secrets.HCLOUD_SERVER_TYPE }}
|
|
@@ -117,7 +118,6 @@ jobs:
|
|
|
117
118
|
run: |
|
|
118
119
|
export AI_API_KEY="${AI_API_KEY:-$SHARED_AI_API_KEY}"
|
|
119
120
|
export GIT_SYNC_TOKEN="${GIT_SYNC_TOKEN:-$SHARED_GIT_SYNC_TOKEN}"
|
|
120
|
-
export MCP_AUTH_TOKEN="${MCP_AUTH_TOKEN:-$SHARED_MCP_AUTH_TOKEN}"
|
|
121
121
|
bun deploy/scripts/validate-secrets.ts
|
|
122
122
|
|
|
123
123
|
- name: Seed content repo
|
|
@@ -175,14 +175,14 @@ jobs:
|
|
|
175
175
|
env:
|
|
176
176
|
SHARED_AI_API_KEY: ${{ secrets[steps.user_secrets.outputs.shared_ai_api_key_secret_name] }}
|
|
177
177
|
SHARED_GIT_SYNC_TOKEN: ${{ secrets[steps.user_secrets.outputs.shared_git_sync_token_secret_name] }}
|
|
178
|
-
|
|
178
|
+
SETUP_EMAIL_API_KEY: ${{ secrets.SETUP_EMAIL_API_KEY }}
|
|
179
|
+
SETUP_EMAIL_FROM: ${{ secrets.SETUP_EMAIL_FROM }}
|
|
179
180
|
KAMAL_REGISTRY_PASSWORD: ${{ secrets.KAMAL_REGISTRY_PASSWORD }}
|
|
180
181
|
CERTIFICATE_PEM: ${{ secrets.CERTIFICATE_PEM }}
|
|
181
182
|
PRIVATE_KEY_PEM: ${{ secrets.PRIVATE_KEY_PEM }}
|
|
182
183
|
run: |
|
|
183
184
|
export AI_API_KEY="${AI_API_KEY:-$SHARED_AI_API_KEY}"
|
|
184
185
|
export GIT_SYNC_TOKEN="${GIT_SYNC_TOKEN:-$SHARED_GIT_SYNC_TOKEN}"
|
|
185
|
-
export MCP_AUTH_TOKEN="${MCP_AUTH_TOKEN:-$SHARED_MCP_AUTH_TOKEN}"
|
|
186
186
|
bun deploy/scripts/write-kamal-secrets.ts
|
|
187
187
|
|
|
188
188
|
- name: Provision server
|
|
@@ -20,7 +20,6 @@ const pilot = parseFlatYaml(readFileSync("pilot.yaml", "utf8"));
|
|
|
20
20
|
|
|
21
21
|
writeGitHubEnv("AI_API_KEY", secrets["aiApiKey"] ?? "");
|
|
22
22
|
writeGitHubEnv("GIT_SYNC_TOKEN", secrets["gitSyncToken"] ?? "");
|
|
23
|
-
writeGitHubEnv("MCP_AUTH_TOKEN", secrets["mcpAuthToken"] ?? "");
|
|
24
23
|
writeGitHubEnv("DISCORD_BOT_TOKEN", secrets["discordBotToken"] ?? "");
|
|
25
24
|
|
|
26
25
|
writeGitHubOutput(
|
|
@@ -31,10 +30,6 @@ writeGitHubOutput(
|
|
|
31
30
|
"shared_git_sync_token_secret_name",
|
|
32
31
|
requireFlatValue(pilot, "gitSyncToken", "pilot.yaml"),
|
|
33
32
|
);
|
|
34
|
-
writeGitHubOutput(
|
|
35
|
-
"shared_mcp_auth_token_secret_name",
|
|
36
|
-
requireFlatValue(pilot, "mcpAuthToken", "pilot.yaml"),
|
|
37
|
-
);
|
|
38
33
|
|
|
39
34
|
function extractAgeIdentity(contents: string): string {
|
|
40
35
|
const line = contents
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
2. Run `bunx brains-ops age-key:bootstrap <repo> --push-to gh`.
|
|
5
5
|
3. Fill in `pilot.yaml`.
|
|
6
6
|
- keep your pinned `brainVersion`
|
|
7
|
-
- confirm shared selectors for `aiApiKey`, `gitSyncToken`,
|
|
7
|
+
- confirm shared selectors for `aiApiKey`, `gitSyncToken`, and `contentRepoAdminToken`
|
|
8
8
|
- use different tokens for `contentRepoAdminToken` and `gitSyncToken`: admin creates/checks content repos; sync is used by runtime directory-sync
|
|
9
9
|
- confirm `agePublicKey`
|
|
10
10
|
4. Run `bunx brains-ops user:add <repo> <handle> --cohort <cohort>`.
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
- if the user should be an anchor there, add `--anchor-id <discord-user-id>`.
|
|
13
13
|
- the command creates `users/<handle>.yaml`, `users/<handle>.secrets.yaml`, and the cohort membership without duplicating existing entries.
|
|
14
14
|
5. Edit the generated user file if the anchor profile needs richer metadata.
|
|
15
|
+
- For browser/CMS-first onboarding, add `setup.delivery: email` and `setup.email` to the user file.
|
|
16
|
+
- Ensure `SETUP_EMAIL_API_KEY` and `SETUP_EMAIL_FROM` exist as GitHub Secrets before deploying any email-setup user.
|
|
15
17
|
6. Run `bunx brains-ops render <repo>`.
|
|
16
18
|
7. Run `bunx brains-ops ssh-key:bootstrap <repo> --push-to gh`.
|
|
17
19
|
8. Run `bunx brains-ops cert:bootstrap <repo> --push-to gh`.
|
|
@@ -23,11 +25,11 @@
|
|
|
23
25
|
- `https://<handle>.rizom.ai/health` returns `200`
|
|
24
26
|
- unauthenticated `POST https://<handle>.rizom.ai/mcp` returns `401`
|
|
25
27
|
14. For fleet upgrades, edit `pilot.yaml.brainVersion` and push once; CI rebuilds the shared image tag, refreshes generated user env files, and redeploys affected users.
|
|
26
|
-
15.
|
|
28
|
+
15. For Discord users, hand the Discord setup details to the user. For email-setup users, confirm they received the setup email and completed passkey registration.
|
|
27
29
|
16. Hand over the browser defaults:
|
|
28
30
|
- Dashboard: `https://<handle>.rizom.ai/`
|
|
29
31
|
- CMS: `https://<handle>.rizom.ai/cms`
|
|
30
32
|
- GitHub token guidance for CMS access to the user's private content repo
|
|
31
|
-
17. If they need direct client access,
|
|
33
|
+
17. If they need direct client access, use OAuth/passkey-capable clients where possible; do not use the deprecated static `MCP_AUTH_TOKEN` path for Rover pilot users.
|
|
32
34
|
18. If you are also giving them a content repo workflow, describe it as optional and frame git/Obsidian as an advanced file-based path, not the default.
|
|
33
35
|
19. Send `docs/user-onboarding.md` to the user as the pilot handoff guide.
|
|
@@ -70,6 +70,69 @@ Use these checks after deploy:
|
|
|
70
70
|
- unauthenticated `POST https://<handle>.rizom.ai/mcp` should return `401 Unauthorized: Bearer token required`
|
|
71
71
|
- a bare `GET /` may also return `401`; that is expected for rover core and does not indicate a bad deploy
|
|
72
72
|
|
|
73
|
+
## Setup email checklist
|
|
74
|
+
|
|
75
|
+
Use this for browser/CMS-first users who should receive their own first-passkey setup link by email.
|
|
76
|
+
|
|
77
|
+
1. Add setup delivery to the user file:
|
|
78
|
+
|
|
79
|
+
```yaml
|
|
80
|
+
setup:
|
|
81
|
+
delivery: email
|
|
82
|
+
email: user@example.com
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
2. Configure these GitHub Secrets before deploy:
|
|
86
|
+
- `SETUP_EMAIL_API_KEY`
|
|
87
|
+
- `SETUP_EMAIL_FROM`
|
|
88
|
+
|
|
89
|
+
3. Reconcile/deploy the user or cohort:
|
|
90
|
+
- `bunx brains-ops onboard . <handle>`
|
|
91
|
+
- or `bunx brains-ops reconcile-cohort . <cohort>`
|
|
92
|
+
|
|
93
|
+
4. Verify the generated `users/<handle>/brain.yaml` contains `auth-service.setupEmail` and `email-resend` config.
|
|
94
|
+
5. Ask the user to complete passkey setup from the email link, then use:
|
|
95
|
+
- Dashboard: `https://<handle>.rizom.ai/`
|
|
96
|
+
- CMS: `https://<handle>.rizom.ai/cms`
|
|
97
|
+
|
|
98
|
+
Notes:
|
|
99
|
+
|
|
100
|
+
- The setup URL is generated and sent by the running brain; operators should not scrape logs or SSH into the instance to retrieve it.
|
|
101
|
+
- The auth service owns setup email dedupe. It should not resend for the same persisted setup token after restart, but should retry failed delivery and resend after token rotation.
|
|
102
|
+
- `SETUP_EMAIL_FROM` is not marked required because fleets without email setup can omit it, but it is required for users with `setup.delivery: email`.
|
|
103
|
+
|
|
104
|
+
## Legacy MCP token cleanup
|
|
105
|
+
|
|
106
|
+
Rover pilot onboarding no longer uses the deprecated static `MCP_AUTH_TOKEN` fallback. OAuth/passkeys and setup email are the default browser/CMS path.
|
|
107
|
+
|
|
108
|
+
For existing Rover pilot repos:
|
|
109
|
+
|
|
110
|
+
1. Update the checked-in deploy contract first:
|
|
111
|
+
- remove `mcpAuthToken` from `pilot.yaml`
|
|
112
|
+
- remove `MCP_AUTH_TOKEN` from `.env.schema`
|
|
113
|
+
- remove `SHARED_MCP_AUTH_TOKEN` / `MCP_AUTH_TOKEN` exports from `.github/workflows/deploy.yml`
|
|
114
|
+
- update `deploy/scripts/decrypt-user-secrets.ts` so it no longer reads or writes `mcpAuthToken`
|
|
115
|
+
|
|
116
|
+
2. Confirm no per-user or cohort MCP overrides exist:
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
rg "mcpAuthToken|MCP_AUTH_TOKEN" users cohorts pilot.yaml
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
3. If there were no user/cohort overrides, no `.age` re-encryption is needed: the default token lived only as the GitHub Secret named `MCP_AUTH_TOKEN`, not inside `users/<handle>.secrets.yaml.age`.
|
|
123
|
+
4. Redeploy all existing Rover users while the GitHub Secret still exists. A secret existing in GitHub is not inherited by jobs or containers unless the workflow references it.
|
|
124
|
+
5. Verify the new deploy does not pass the token:
|
|
125
|
+
- generated `.kamal/secrets` does not contain `MCP_AUTH_TOKEN`
|
|
126
|
+
- the running container environment does not contain `MCP_AUTH_TOKEN`
|
|
127
|
+
|
|
128
|
+
6. Delete the unused GitHub Secret last:
|
|
129
|
+
|
|
130
|
+
```sh
|
|
131
|
+
gh secret delete MCP_AUTH_TOKEN
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Only decrypt and re-encrypt `users/<handle>.secrets.yaml.age` files if step 2 or a direct audit shows an actual `mcpAuthToken` override was stored there.
|
|
135
|
+
|
|
73
136
|
## Discord bot token checklist
|
|
74
137
|
|
|
75
138
|
Use this when enabling Discord for a pilot user.
|
|
@@ -97,7 +160,7 @@ Notes:
|
|
|
97
160
|
- Do not reuse the same Discord bot token across multiple pilot users.
|
|
98
161
|
- Discord is the default pilot interface moving forward.
|
|
99
162
|
- The encrypted `users/<handle>.secrets.yaml.age` file is the durable checked-in deploy input; your local env is only the operator staging source.
|
|
100
|
-
- MCP
|
|
163
|
+
- Direct MCP client access should use OAuth/passkey-capable clients where possible; do not reintroduce `MCP_AUTH_TOKEN` for Rover pilot users.
|
|
101
164
|
- When explaining the content workflow, describe it first as a normal **git repo** of **markdown/text files**.
|
|
102
165
|
- Position **Obsidian** as optional: it is just one possible editor for those same files, not the default requirement.
|
|
103
166
|
|