Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
116 changes: 116 additions & 0 deletions .agents/skills/bump-version/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
---
name: bump-version
description: Bump the release version everywhere in the Brain3 project — Cargo.toml, pyproject.toml, README, first_run_setup.rs, and test fixtures — then execute the full release process: tag, CI workflow, and verification.
---

# bump-version / release

Full release process for Brain3. Execute each step using the Bash tool — do not show commands for the user to copy-paste, run them directly.

## Files updated by the bump script

| File | What changes |
|------|-------------|
| `apps/gateway/Cargo.toml` | `version = "X.Y.Z"` |
| `brain3-mcp-vault-tools/pyproject.toml` | `version = "X.Y.Z"` |
| `README.MD` | S3 install URL (`/releases/vX.Y.Z/install.sh`) |
| `crates/core/src/application/first_run_setup.rs` | `CURRENT_RELEASE = "vX.Y.Z"` |
| `brain3-mcp-vault-tools/tests/test_server_startup.py` | Two version string fixtures |
| `Cargo.lock` | Auto-updated via `cargo fetch` |
| `brain3-mcp-vault-tools/uv.lock` | Regenerated via `uv lock --project ./brain3-mcp-vault-tools` |

---

## Step 0 — Ensure we're on a release branch

Before bumping anything, check the current branch:

```bash
git rev-parse --abbrev-ref HEAD
```

The version bump must happen on a dedicated release branch named `bump_version_<XYZ>` (the version with dots removed — e.g. `0.2.8` → `bump_version_028`), **not** on `main`.

- If already on the expected `bump_version_<XYZ>` branch, proceed to Step 1.
- Otherwise (on `main` or any other branch), **stop and offer to create the branch** for the user. Do not create it without confirmation. Once confirmed, run:

```bash
git checkout -b bump_version_<XYZ>
```

Only after we're on the release branch should you continue to Step 1.

---

## Step 1 — Bump version in files

Run the bump script (it detects the current version from `apps/gateway/Cargo.toml`, applies all replacements, and refreshes `Cargo.lock`):

```bash
bash .Codex/skills/bump-version/bump.sh VERSION
```

Then refresh and verify the Python lockfile (pyproject.toml was just bumped, so uv.lock will be stale):

```bash
uv lock --project ./brain3-mcp-vault-tools
uv lock --project ./brain3-mcp-vault-tools --check
```

Then run `cargo test` to verify nothing is broken.

Do NOT commit — tell the user to commit themselves, as per project instructions. Remind them that both `brain3-mcp-vault-tools/pyproject.toml` and `brain3-mcp-vault-tools/uv.lock` must be included in the commit.

---

## Step 2 — Tag and push (triggers CI release workflow)

**The tag does not require the PR to be merged first.** The release workflow builds from whatever commit the tag points at, so you can tag the `bump_version_<XYZ>` branch HEAD directly — even while its PR is still open or blocked on review. Don't wait for the merge to cut the release.

Ask the user to confirm before tagging and pushing. Once confirmed, run (tags the current HEAD, i.e. the release branch commit):

```bash
VERSION=vX.Y.Z
git tag -a "$VERSION" -m "Release $VERSION"
git push origin "$VERSION"
```

Pushing the tag triggers `.github/workflows/release.yml`, which builds all four targets, signs `SHA256SUMS`, and publishes to GitHub Releases and S3 (~5–10 min).

Then monitor progress by running:

```bash
gh run watch
```

---

## Step 3 — Verify

Once the workflow completes, run:

```bash
VERSION=vX.Y.Z
gh release view "$VERSION"
```

Report the listed assets to the user.

Then verify the Docker image published to GHCR by pulling it:

```bash
VERSION=vX.Y.Z
docker pull ghcr.io/tleyden/brain3-mcp-vault-tools:$VERSION
```

Confirm the pull succeeds and report the digest to the user.

---

## Gotchas

- The version argument can have or omit a leading `v` — both `0.2.3` and `v0.2.3` work.
- Push only the tag (`git push origin "$VERSION"`), not all branches — pushing everything can trigger unintended workflows.
- Tag and push **after** the user has committed the version bump.
- `cargo fetch` (not `cargo build`) is used to update `Cargo.lock` quickly.
- Any change to `brain3-mcp-vault-tools/pyproject.toml` or other package metadata requires regenerating `brain3-mcp-vault-tools/uv.lock` in the same commit — `uv lock --project ./brain3-mcp-vault-tools --check` will fail in CI otherwise.
77 changes: 77 additions & 0 deletions .agents/skills/bump-version/bump.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
set -euo pipefail

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"

usage() {
echo "Usage: $0 <new-version>"
echo ""
echo " <new-version> New version without 'v' prefix, e.g. 0.2.3"
echo ""
echo "The script will update all version references, then ask before tagging and pushing."
exit 1
}

[ $# -lt 1 ] && usage

NEW="${1#v}" # strip leading 'v' if present

cd "$REPO_ROOT"

# Detect current version from gateway Cargo.toml
OLD=$(grep '^version' apps/gateway/Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')

if [ "$OLD" = "$NEW" ]; then
echo "Already at version $NEW — nothing to do."
exit 0
fi

echo "Bumping $OLD → $NEW"

# 1. apps/gateway/Cargo.toml
sed -i.bak "s/^version = \"$OLD\"/version = \"$NEW\"/" apps/gateway/Cargo.toml
rm -f apps/gateway/Cargo.toml.bak

# 2. brain3-mcp-vault-tools/pyproject.toml
sed -i.bak "s/^version = \"$OLD\"/version = \"$NEW\"/" brain3-mcp-vault-tools/pyproject.toml
rm -f brain3-mcp-vault-tools/pyproject.toml.bak

# 3. README.MD (URL contains v-prefixed version)
sed -i.bak "s|/v${OLD}/|/v${NEW}/|g" README.MD
rm -f README.MD.bak

# 4. crates/core/src/application/first_run_setup.rs (v-prefixed)
sed -i.bak "s|\"v${OLD}\"|\"v${NEW}\"|g" crates/core/src/application/first_run_setup.rs
rm -f crates/core/src/application/first_run_setup.rs.bak

# 5. brain3-mcp-vault-tools/tests/test_server_startup.py (bare version, no v)
sed -i.bak "s/\"${OLD}\"/\"${NEW}\"/g" brain3-mcp-vault-tools/tests/test_server_startup.py
rm -f brain3-mcp-vault-tools/tests/test_server_startup.py.bak

# 6. Update Cargo.lock by running cargo fetch (fast, no build needed)
echo "Updating Cargo.lock..."
cargo fetch --quiet 2>&1 | grep -v '^$' || true

echo ""
echo "Done. Files updated:"
echo " apps/gateway/Cargo.toml"
echo " brain3-mcp-vault-tools/pyproject.toml"
echo " README.MD"
echo " crates/core/src/application/first_run_setup.rs"
echo " brain3-mcp-vault-tools/tests/test_server_startup.py"
echo " Cargo.lock"
echo ""
echo "Review the diff, then commit with:"
echo " git commit -am \"bump version $NEW\""
echo ""

read -rp "Tag and push v${NEW} now? [y/N] " CONFIRM
if [[ "${CONFIRM,,}" == "y" ]]; then
git tag -a "v${NEW}" -m "Release v${NEW}"
git push origin "v${NEW}"
echo "Tagged and pushed v${NEW}. Monitor the release workflow with: gh run watch"
else
echo "Skipped tagging. To tag later:"
echo " git tag -a \"v${NEW}\" -m \"Release v${NEW}\""
echo " git push origin \"v${NEW}\""
fi
74 changes: 74 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,37 @@ brain3.yourdomain.com {

</details>

<details>
<summary>Experimental: Plugin MCP Containers</summary>

Plugin MCP Containers are unsupported and subject to change. They are configured
only by hand-editing `brain3.yaml` in your Brain3 home directory, usually
`~/.brain3/brain3.yaml`.

Today Brain3 reads only the `plugin_mcp_containers` section:

```yaml
plugin_mcp_containers:
- name: hello_mcp
platform: docker
image: ghcr.io/example/hello-mcp
tag: latest
port: 8420
host_directory: /Users/you/hello-mcp-data
container_directory: /data
auth:
type: bearer_token
secret_file: /Users/you/.brain3/secrets/hello_mcp.token
secret_mount_path: /run/secrets/mcp_bearer_token
```

`name` must use lowercase letters, digits, and underscores only. Brain3 starts
each configured Plugin MCP Container with one read-write host directory mount,
sends `Authorization: Bearer <token>` when `auth.type` is `bearer_token`, and
exposes its tools with a `{name}__` prefix.

</details>

## Privacy & Security

<details>
Expand Down
7 changes: 7 additions & 0 deletions SECURITY_AUDIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Brain3’s highest-risk boundary is the optional public gateway/tunnel that fron
- Local `.env`, Cloudflare tunnel config, and SQLite token database files
- Gateway public origin, Cloudflare tunnel identity, and container network isolation state
- Native Whisper model files loaded by the gateway process for audio transcription
- Experimental Plugin MCP Container config at `<app_home>/brain3.yaml`, referenced container images, referenced bearer-token secret files, and host directories mounted into those containers

### Trust Boundaries

Expand All @@ -62,6 +63,7 @@ Brain3’s highest-risk boundary is the optional public gateway/tunnel that fron
- Local host filesystem and temp-directory principals
- Local process-control principals that can signal the running gateway process
- The boundary between the Rust gateway and the `brain3-mcp-vault-tools` container
- The boundary between the Rust gateway and any opt-in Plugin MCP Containers started from `<app_home>/brain3.yaml`
- The native transcription boundary where the Rust gateway downloads and parses untrusted audio bytes in-process instead of forwarding them to the container
- Gateway-initiated internet egress for temporary audio `download_url` fetches and setup-time Whisper model downloads
- Vault content that may be user-controlled or, in some deployments, third-party-controlled
Expand All @@ -72,6 +74,7 @@ Brain3’s highest-risk boundary is the optional public gateway/tunnel that fron
- Operate or compromise the preregistered OAuth client after it is provisioned with Brain3 credentials
- Supply a `transcribe_audio_file` audio `download_url` through an authorized MCP client, causing the gateway to issue an outbound GET and parse the returned bytes as audio
- Read local files or logs available to the current OS principal or broader local principals
- Write `<app_home>/brain3.yaml`, any referenced bearer-token secret file, or any referenced Plugin MCP Container image/tag. A principal with that capability can cause Brain3 to run arbitrary Docker/macOS-container images and mount the configured `host_directory` read-write into those containers, with the effective privileges of the selected container runtime and the user running Brain3.
- Supply hostile vault content when the user does not fully control imported or shared notes

### Security Objectives
Expand All @@ -80,6 +83,7 @@ Brain3’s highest-risk boundary is the optional public gateway/tunnel that fron
- Public ingress should be opt-in and should not broaden exposure accidentally
- Local secrets and vault contents should not leak through logs or insecure default storage/permissions
- Container networking should keep the MCP server private by default
- Plugin MCP Containers should remain an opt-in, local-file-only experimental surface; there must be no remote API, setup wizard path, or dynamic client registration path that can add or modify these containers.
- Native transcription should enforce bounded downloads and reject bytes that cannot be decoded as audio before running Whisper inference

### Assumptions
Expand All @@ -91,6 +95,9 @@ Brain3’s highest-risk boundary is the optional public gateway/tunnel that fron
- Whisper model downloads are setup-time, user-initiated egress and must be checksum-verified before model files are treated as usable.
- Prompt injection is generally out of scope for user-controlled vault content, but not for vaults the user does not fully control
- The gateway's SIGUSR1 diagnostics trigger is local-only: a sender must already be able to signal the gateway process, and the dump is written only to that process's stdout plus a log-file marker. The dump includes the MCP container's own logs, so the container must continue to avoid logging secrets; trace-level body logging remains covered by Finding 4.
- Plugin MCP Containers are Experimental and intentionally undocumented outside the README experimental section. They are configured only by hand-editing local files under `<app_home>` and are trusted at the same level as the core vault MCP container once started.
- Plugin MCP Container tool metadata and tool results are not sandboxed or semantically vetted by Brain3 before being appended to `tools/list` or returned from `tools/call`. This matches the current trust model for the vault container's tools and must be treated as trusted local-container output, not as untrusted internet content.
- Docker-backed Plugin MCP Containers on macOS are reached through a loopback-published host port because Docker Desktop does not expose published ports from internal-only networks. This remains local-only ingress, but it is not the same internal-network-only posture used by Linux Docker Plugin MCP Containers.

## Findings

Expand Down
Loading
Loading