Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
47961fb
feat(cli): add get-token subcommand with auto-capture via volume mount
aliasunder Jul 14, 2026
d2a9f7b
Merge branch 'main' into worktree-cli-get-token
aliasunder Jul 16, 2026
679a0de
fix(review): use function replacement in patchEnvObsidianToken to avo…
aliasunder Jul 16, 2026
cf2e9a0
style: use existing GetTokenDeps type instead of inline duplicate
aliasunder Jul 16, 2026
3d54288
test: exact assertions on deterministic messages, $-pattern regressio…
aliasunder Jul 16, 2026
45d3460
fix: correct cli README claim that get-token runs automatically durin…
aliasunder Jul 16, 2026
16f6cb2
fix(cli): address bot review findings — catch capture errors, warn on…
aliasunder Jul 16, 2026
20f6c88
refactor(cli): flatten captureObsidianToken and stop echoing the capt…
aliasunder Jul 16, 2026
5fe1a55
refactor(cli): replace catch-all in token capture with per-operation …
aliasunder Jul 16, 2026
494a44e
refactor(cli): return undefined instead of empty-string sentinel from…
aliasunder Jul 16, 2026
36c7e96
style(cli): name the login-container result for readability
aliasunder Jul 16, 2026
943861a
style(cli): rename succeeded to loginSucceeded per naming conventions
aliasunder Jul 16, 2026
1824dd7
refactor(cli): rename get-token subcommand to get-sync-token
aliasunder Jul 16, 2026
cc166e9
refactor(docker): rename and rewrite the rootfs token helper as get-s…
aliasunder Jul 16, 2026
56971b4
refactor(docker): rewrite the two inherited s6 init scripts in our ow…
aliasunder Jul 16, 2026
f027ba2
docs(deploy): reference the CLI for Sync-token generation in DEPLOY.md
aliasunder Jul 16, 2026
755885d
docs(cli): reword token-capture copy in user-facing messages
aliasunder Jul 16, 2026
9b08e1f
feat(cli): state the token destination in the login handoff message
aliasunder Jul 16, 2026
36a39b7
test(cli): replace awkward token-destination fixture string with a re…
aliasunder Jul 16, 2026
ed470ab
Merge branch 'main' into worktree-cli-get-token
aliasunder Jul 16, 2026
5c02218
Merge branch 'main' into worktree-cli-get-token
aliasunder Jul 16, 2026
48f6a8c
Merge branch 'main' into worktree-cli-get-token
aliasunder Jul 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ PUBLIC_URL=

# Obsidian Sync credentials
# Generate OBSIDIAN_AUTH_TOKEN once with:
# docker run --rm -it --entrypoint get-token \
# docker run --rm -it --entrypoint get-sync-token \
# ghcr.io/aliasunder/vault-cortex:remote
OBSIDIAN_AUTH_TOKEN=
VAULT_NAME=
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ obsidian-headless/ # Lockfile-pinned obsidian-headless for D
package-lock.json # sha512 integrity hashes (supply-chain security)
rootfs/ # Container filesystem overlay (remote target)
etc/s6-overlay/ # init chain + svc-obsidian-sync + svc-vault-mcp
usr/local/bin/get-token # interactive Obsidian Sync token helper
usr/local/bin/get-sync-token # interactive Obsidian Sync token helper (manual flow)
docker-compose.yml # Lightsail: single vault-cortex:remote service
docker-compose.local.yml # Contributor dev: builds from source
.env.example # template for Lightsail .env
Expand Down Expand Up @@ -85,6 +85,7 @@ cli/ # npx vault-cortex CLI (published as vaul
scaffold.ts # File generation (.env)
docker.ts # Container management (docker run, health-check wait)
upgrade.ts # Upgrade command (pull + re-create + health check)
get-sync-token.ts # Get-sync-token subcommand (Sync token auto-capture via volume mount)
env.ts # Environment file handling (.env generation)
token.ts # Secure token generation (openssl rand)
vault.ts # Vault path validation
Expand Down
52 changes: 26 additions & 26 deletions ARCHITECTURE.md

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions DEPLOY.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,13 @@ Then open `~/.config/vault-cortex/.env` and fill in the remaining values:
The [`.env.example`](./.env.example) file also includes optional configuration for the embedding pipeline (`EMBEDDING_ENABLED`), the reranker (`RERANK_MODE`), the memory system (`MEMORY_ENABLED`, `MEMORY_DIR`, `PROTECTED_PATHS`, `ORPHAN_EXCLUDE_FOLDERS`), timezone (`TZ`), and OAuth metadata (`SERVICE_DOCUMENTATION_URL`). All have sensible defaults — see the [Configuration](./README.md#configuration) section in the README.

```bash
docker run --rm -it --entrypoint get-token ghcr.io/aliasunder/vault-cortex:remote
npx vault-cortex get-sync-token
```

Or run the Docker image directly:

```bash
docker run --rm -it --entrypoint get-sync-token ghcr.io/aliasunder/vault-cortex:remote
```

**4. Authenticate to GHCR** (once per machine):
Expand Down Expand Up @@ -280,7 +286,7 @@ To find your stage: `cat .sst/stage` (after your first deploy).
| `GHCR_TOKEN` | Personal access token (classic) with `write:packages` + `read:packages`. Used by `docker login` both at build-push and on-instance pull. Persists across runs; rotate when stale. |
| `DOCKERHUB_TOKEN` | Optional. Docker Hub access token with `Read & Write` repository permissions. Used by deploy (image push) and dockerhub-description (DOCKERHUB.md sync). Only needed when `DOCKERHUB_USERNAME` is set. |
| `MCP_AUTH_TOKEN` | Same value as the SST secret of the same name. Written into the instance `.env` for the Express auth layer. |
| `OBSIDIAN_AUTH_TOKEN` | Output of `docker run --rm -it --entrypoint get-token ghcr.io/aliasunder/vault-cortex:remote`. |
| `OBSIDIAN_AUTH_TOKEN` | Output of `npx vault-cortex get-sync-token` — see [One-time setup](#one-time-setup). |
| `VAULT_PASSWORD` | Optional. Only set if your vault uses end-to-end encryption. Empty value is fine and ships through to `.env` as `VAULT_PASSWORD=`. |
| `SSH_PUBKEY` | Public key contents of your `~/.ssh/vault-cortex.pub` (literal, single line). Same key local dev and CI use — see [Prerequisites](#prerequisites). |
| `SSH_PRIVATE_KEY` | Private half (`~/.ssh/vault-cortex`, full multi-line block including BEGIN/END markers). Loaded by `webfactory/ssh-agent` for SCP/SSH to the instance. |
Expand Down
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ EXPOSE 8000
# ---------------------------------------------------------------------------
# remote: s6-overlay supervises obsidian-sync + vault-mcp in one container.
# Absorbed from aliasunder/obsidian-headless-sync-docker (rootfs/, including
# the get-token helper), adapted from Alpine to Debian.
# the interactive token helper, since rewritten as get-sync-token), adapted from Alpine to Debian.
# ---------------------------------------------------------------------------
FROM base AS remote

Expand Down Expand Up @@ -140,9 +140,9 @@ RUN userdel -r node \
&& chown -R obsidian:obsidian /vault /home/obsidian

# s6 service definitions (init chain + svc-obsidian-sync + svc-vault-mcp)
# and the interactive get-token helper (rootfs/usr/local/bin/get-token).
# and the interactive get-sync-token helper (rootfs/usr/local/bin/get-sync-token).
COPY rootfs/ /
RUN chmod +x /usr/local/bin/get-token \
RUN chmod +x /usr/local/bin/get-sync-token \
&& chmod +x /etc/s6-overlay/scripts/* \
&& chmod +x /etc/s6-overlay/s6-rc.d/svc-obsidian-sync/run \
/etc/s6-overlay/s6-rc.d/svc-vault-mcp/run
Expand Down
20 changes: 20 additions & 0 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,26 @@ container; this CLI scaffolds the config so you don't have to.

Existing files are never overwritten without asking.

## Get Sync Token

Generate an Obsidian Sync auth token without leaving the CLI:

```bash
npx vault-cortex get-sync-token
```

The command opens the Obsidian login inside Docker. Once you've signed
in, it captures your token and prints it — nothing to dig out of the
login output. Use `--dir` to write the token straight into an existing
`.env` instead:

```bash
npx vault-cortex get-sync-token --dir ./vault-cortex
```

During `init --mode remote`, this flow is offered automatically when Docker
is available.

## Upgrade

Pull the latest image, re-create the container, and verify health:
Expand Down
66 changes: 66 additions & 0 deletions cli/src/__tests__/docker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest"

import {
buildDockerRunArgs,
buildObsidianLoginArgs,
CONTAINER_NAME,
LOCAL_IMAGE,
pollHealth,
Expand Down Expand Up @@ -187,6 +188,71 @@ describe("buildDockerRunArgs", () => {
})
})

describe("buildObsidianLoginArgs", () => {
it("produces the correct args on macOS (no --user flag)", () => {
const args = buildObsidianLoginArgs({
configMountPath: "/tmp/vault-cortex-sync-token-abc",
platform: "darwin",
uid: 501,
gid: 20,
})

expect(args).toEqual([
"run",
"--rm",
"-it",
"--entrypoint",
"ob",
"-v",
"/tmp/vault-cortex-sync-token-abc:/home/obsidian/.config",
REMOTE_IMAGE,
"login",
])
})

it("includes --user uid:gid on Linux", () => {
const args = buildObsidianLoginArgs({
configMountPath: "/tmp/vault-cortex-sync-token-abc",
platform: "linux",
uid: 1000,
gid: 1000,
})

expect(args).toEqual([
"run",
"--rm",
"-it",
"--entrypoint",
"ob",
"-v",
"/tmp/vault-cortex-sync-token-abc:/home/obsidian/.config",
"--user",
"1000:1000",
REMOTE_IMAGE,
"login",
])
})

it("omits --user on Linux when uid/gid are not provided", () => {
const args = buildObsidianLoginArgs({
configMountPath: "/tmp/test",
platform: "linux",
})

expect(args).toEqual([
"run",
"--rm",
"-it",
"--entrypoint",
"ob",
"-v",
"/tmp/test:/home/obsidian/.config",
REMOTE_IMAGE,
"login",
])
})
})

describe("pollHealth", () => {
it("returns true as soon as the endpoint responds ok", async () => {
const fetchStub = async (): Promise<Response> => okResponse
Expand Down
5 changes: 1 addition & 4 deletions cli/src/__tests__/env.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { describe, expect, it } from "vitest"

import { REMOTE_IMAGE } from "../docker.js"
import { buildLocalEnv, buildRemoteEnv } from "../env.js"

describe("buildLocalEnv", () => {
Expand Down Expand Up @@ -81,9 +80,7 @@ describe("buildRemoteEnv", () => {

expect(env).toMatch(/^OBSIDIAN_AUTH_TOKEN=$/m)
expect(env).toContain("FILL THIS IN")
// The guidance must name the actual get-token image, not just the
// subcommand — "get-token" alone would pass with a stale image name.
expect(env).toContain(`get-token \\\n# ${REMOTE_IMAGE}`)
expect(env).toContain("npx vault-cortex get-sync-token")
})

it("states defaulted sync settings as uncommented lines", () => {
Expand Down
Loading
Loading