Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
fbc3f7f
fix: patch Debian base image and Node dependency CVEs
bbossa Sep 5, 2026
9738755
feat(TASK-138): add browser companion protocol
goniszewski Sep 7, 2026
d697f0b
feat(TASK-138): support rich browser captures
goniszewski Sep 7, 2026
1c6d44b
test(TASK-138): align browser integration docs check
goniszewski Sep 7, 2026
0b4ae53
docs(TASK-138): clarify integration token usage
goniszewski Sep 7, 2026
369ad1b
chore(release): prepare Grimoire 1.2.0
goniszewski Sep 7, 2026
c1b9bd3
fix(TASK-138): support Safari Companion origins
goniszewski Sep 9, 2026
828f3b1
Merge pull request #219 from goniszewski/feat/task-138-browser-extens…
goniszewski Sep 9, 2026
3149d2e
fix(deps): remediate release dependency advisories
goniszewski Sep 9, 2026
602925a
Merge pull request #220 from goniszewski/fix/v1.2.0-dependency-adviso…
goniszewski Sep 9, 2026
9f09dde
feat: add Homebrew support and rename CLI to Grimoire
goniszewski Aug 30, 2026
bc891c0
test: add Homebrew tap smoke coverage
goniszewski Aug 30, 2026
c2cdf74
fix(homebrew): block native updates and correct formula tests
goniszewski Aug 31, 2026
52acee7
fix(homebrew): harden installer and smoke validation
goniszewski Sep 6, 2026
49fcfbb
fix: prepare and validate Homebrew installation for v1.2
goniszewski Sep 9, 2026
be9819c
fix(settings): enable model connection retry
goniszewski Sep 12, 2026
547bfd8
fix(search): default queries to relevance while preserving library sort
goniszewski Sep 17, 2026
219f374
fix(homebrew): trust formula before tapping and test candidate archives
goniszewski Sep 17, 2026
328f466
fix(release): pin signed v1.2 archives and isolate Homebrew cleanup
goniszewski Sep 17, 2026
00b9613
fix(ci): pin Bun setup and use absolute installer shell paths
goniszewski Sep 17, 2026
ca974f3
Merge pull request #222 from goniszewski/release/v1.2.0-readiness
goniszewski Sep 17, 2026
1bc1488
docs: confirm published macOS Homebrew installation
goniszewski Sep 17, 2026
b6cd52e
test(docs): reflect verified macOS Homebrew release status
goniszewski Sep 17, 2026
8c704c1
Merge pull request #223 from goniszewski/docs/v1.2.0-published-homebrew
goniszewski Sep 17, 2026
31d4696
docs: add v1.2 upstream catch-up runbook and sync-check workflow
Sep 18, 2026
e5c9f50
Merge remote-tracking branch 'upstream/main' into sync/upstream-v1.2
Sep 18, 2026
1c7026f
Merge remote-tracking branch 'origin/fix/security-cve-2026-09' into s…
Sep 18, 2026
3eb3c91
ci: add dedicated Trivy scan workflow
Sep 18, 2026
c8ddfc8
ci: fix SonarCloud findings on new workflow files
Sep 18, 2026
9611d79
chore: regenerate root bun.lock for upstream v1.2 dependency bumps
Sep 18, 2026
b5027db
fix: override transitive lodash to remediate CVE-2026-4800
Sep 18, 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
41 changes: 40 additions & 1 deletion .github/workflows/quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
cache: npm

- name: Set up Bun
uses: oven-sh/setup-bun@v2
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2

- name: Install frontend dependencies
run: npm ci --ignore-scripts
Expand Down Expand Up @@ -71,3 +71,42 @@ jobs:
name: grimoire-demo-dist
path: dist/
if-no-files-found: error

homebrew:
name: Homebrew tap and install smoke test
runs-on: macos-latest
env:
HOMEBREW_NO_AUTO_UPDATE: "1"
HOMEBREW_NO_ENV_HINTS: "1"
HOMEBREW_NO_INSTALL_CLEANUP: "1"
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm

- name: Set up Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2

- name: Build candidate archive
run: |
npm ci --ignore-scripts
npm run package:release -- --platform macos
python3 - <<'PYTHON'
import json, pathlib, re
artifact = json.loads(pathlib.Path("release/release-manifest.json").read_text())["artifacts"][0]
formula = pathlib.Path("Formula/grimoire.rb")
text, count = re.subn(r'(sha256 ")[a-f0-9]{64}(")', lambda match: match[1] + artifact["sha256"] + match[2], formula.read_text(), count=1)
assert count == 1
formula.write_text(text)
PYTHON

- name: Validate local tap and install formula
env:
HOMEBREW_TEST_ARCHIVE_DIR: ${{ github.workspace }}/release
HOMEBREW_TEST_EXPECTED_VERSION: 1.2.0
run: bash scripts/homebrew-smoke.sh
50 changes: 50 additions & 0 deletions .github/workflows/trivy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Trivy scan

# Fork-only CI gate: this repo's purpose is to track upstream dependency bumps
# and verify them against Trivy before redeploying. Kept separate from
# quality.yml (upstream-owned) so upstream syncs never conflict here.

on:
pull_request:
push:
branches:
- main
workflow_dispatch: {}

permissions:
contents: read

jobs:
fs-scan:
name: Trivy filesystem scan (dependencies)
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Trivy fs scan
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
scan-type: fs
scan-ref: .
severity: HIGH,CRITICAL
ignore-unfixed: true
exit-code: "1"

image-scan:
name: Build image and Trivy scan
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Build image
run: docker build -t grimoire:ci .

- name: Trivy image scan
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
image-ref: grimoire:ci
severity: HIGH,CRITICAL
ignore-unfixed: true
exit-code: "1"
73 changes: 73 additions & 0 deletions .github/workflows/upstream-sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Upstream sync check

# Watches goniszewski/grimoire (the upstream this repo is forked from) and opens/
# updates a PR whenever upstream/main gets commits this fork doesn't have yet.
# The PR is a signal, not an auto-merge: reconcile it manually following
# CATCHUP-v1.2.md (re-apply the local Trivy CVE patch branch on top).

on:
schedule:
- cron: "0 7 * * 1" # every Monday 07:00 UTC
workflow_dispatch: {}

permissions:
contents: write
pull-requests: write

jobs:
sync:
if: github.repository == 'bbossa/grimoire'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: main

- name: Configure git identity
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

- name: Fetch upstream
run: |
git remote add upstream https://github.com/goniszewski/grimoire.git
git fetch upstream main --tags

- name: Check for new upstream commits
id: diff
run: |
UPSTREAM_SHA=$(git rev-parse upstream/main)
AHEAD=$(git rev-list --count origin/main..upstream/main)
echo "upstream_sha=$UPSTREAM_SHA" >> "$GITHUB_OUTPUT"
echo "ahead=$AHEAD" >> "$GITHUB_OUTPUT"
echo "$AHEAD commit(s) on upstream/main not yet in main (upstream @ $UPSTREAM_SHA)"

- name: Update sync branch and PR
if: steps.diff.outputs.ahead != '0'
env:
GH_TOKEN: ${{ github.token }}
AHEAD: ${{ steps.diff.outputs.ahead }}
UPSTREAM_SHA: ${{ steps.diff.outputs.upstream_sha }}
run: |
git branch -f sync/upstream-main upstream/main
git push origin sync/upstream-main --force

BODY="upstream/main (goniszewski/grimoire) is ${AHEAD} commit(s) ahead of main, at \`${UPSTREAM_SHA}\`.

Do not merge this PR directly: it is a notification branch, not a reconciled one. Follow \`CATCHUP-v1.2.md\` to merge upstream/main and re-apply the local Trivy CVE patch branch (Dockerfile \`apt-get upgrade\`, dependency overrides) on top, then re-scan with Trivy before redeploying."

if gh pr list --head sync/upstream-main --state open --json number -q '.[0].number' | grep -q .; then
echo "Open PR already exists for sync/upstream-main; branch push above updated it."
else
gh pr create \
--base main \
--head sync/upstream-main \
--title "sync: upstream is ${AHEAD} commit(s) ahead" \
--body "$BODY"
fi

- name: Nothing to sync
if: steps.diff.outputs.ahead == '0'
run: echo "main is up to date with upstream/main, nothing to do."
105 changes: 105 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -2401,6 +2401,8 @@ Request body:
| `category_id` | string \| null | no | Existing category ID to assign |
| `category` | string | no | Root category name to resolve or create when category_id is omitted |
| `notes` | string \| null | no | Personal notes, or null to leave empty |
| `is_pinned` | boolean | no | Whether the new bookmark should be pinned |
| `read_later` | boolean | no | Whether the new bookmark should be added to Read Later |
| `source` | CaptureSource | no | |
| `source.client` | string \| null | no | Optional local integration client label |
| `source.source_url` | string \| null | no | Optional public HTTP or HTTPS page/context URL |
Expand Down Expand Up @@ -2481,6 +2483,43 @@ Content-Type: application/json
}
```

#### GET /integrations/browser/v1/capabilities

Negotiate the packaged browser extension capture protocol.

Requires a managed integration bearer token. Packaged Chrome and Firefox clients use this endpoint to distinguish current Grimoire from the legacy account API before sending capture data. The response describes stable paths and enforced field limits; clients must not infer compatibility from the daemon marketing version alone.

Responses:

| Status | Content type | Schema | Description |
|---|---|---|---|
| `200` | application/json | `BrowserIntegrationCapabilitiesResponse` | Supported browser integration capabilities |
| `401` | application/problem+json | `ProblemDetails` | Missing, invalid, rotated, or revoked integration token |

Examples:

**Negotiate browser capture support**

Request:

```bash
curl http://127.0.0.1:3210/integrations/browser/v1/capabilities \
-H "Authorization: Bearer limp_it_example"
```

#### GET /integrations/browser/v1/taxonomy

List the categories and tags available to the packaged browser extension.

Requires a managed integration bearer token. This route keeps extension-origin reads inside the versioned browser integration surface instead of exposing unrelated local-library GET routes.

Responses:

| Status | Content type | Schema | Description |
|---|---|---|---|
| `200` | application/json | `BrowserIntegrationTaxonomyResponse` | Browser capture categories and tags |
| `401` | application/problem+json | `ProblemDetails` | Missing, invalid, rotated, or revoked integration token |

#### GET /capture/bookmarklet

Bookmarklet capture bridge page.
Expand Down Expand Up @@ -3138,6 +3177,8 @@ Protected one-click capture request for explicit local integrations
| `category_id` | string \| null | no | Existing category ID to assign |
| `category` | string | no | Root category name to resolve or create when category_id is omitted |
| `notes` | string \| null | no | Personal notes, or null to leave empty |
| `is_pinned` | boolean | no | Whether the new bookmark should be pinned |
| `read_later` | boolean | no | Whether the new bookmark should be added to Read Later |
| `source` | CaptureSource | no | |
| `source.client` | string \| null | no | Optional local integration client label |
| `source.source_url` | string \| null | no | Optional public HTTP or HTTPS page/context URL |
Expand Down Expand Up @@ -4518,6 +4559,70 @@ Response data
| `uptime` | integer | yes | Process uptime in milliseconds |
| `queueSize` | integer | yes | Queued background jobs |

### BrowserIntegrationCapabilities

Authenticated capabilities advertised to packaged browser extensions

| Field | Type | Required | Description |
|---|---|---:|---|
| `protocol` | "grimoire-browser-capture" | yes | Stable browser integration protocol identifier |
| `protocol_version` | integer | yes | Browser integration protocol version |
| `grimoire_version` | string | yes | Daemon package version |
| `endpoints` | object | yes | |
| `endpoints.capture` | string | yes | Authenticated bookmark capture path |
| `endpoints.taxonomy` | string | yes | Authenticated category and tag discovery path |
| `capture_fields` | object | no | Optional capture fields supported by this daemon |
| `capture_fields.is_pinned` | boolean | yes | Capture accepts an initial pinned state |
| `capture_fields.read_later` | boolean | yes | Capture accepts an initial Read Later state |
| `limits` | object | yes | |
| `limits.request_bytes` | integer | yes | Maximum capture request bytes |
| `limits.title_characters` | integer | yes | Maximum title characters |
| `limits.notes_characters` | integer | yes | Maximum notes characters |
| `limits.selected_text_characters` | integer | yes | Maximum selected text characters |
| `limits.tag_characters` | integer | yes | Maximum characters per tag |

### BrowserIntegrationCapabilitiesResponse

Browser integration capability response

| Field | Type | Required | Description |
|---|---|---:|---|
| `data` | BrowserIntegrationCapabilities | yes | |
| `data.protocol` | "grimoire-browser-capture" | yes | Stable browser integration protocol identifier |
| `data.protocol_version` | integer | yes | Browser integration protocol version |
| `data.grimoire_version` | string | yes | Daemon package version |
| `data.endpoints` | object | yes | |
| `data.endpoints.capture` | string | yes | Authenticated bookmark capture path |
| `data.endpoints.taxonomy` | string | yes | Authenticated category and tag discovery path |
| `data.capture_fields` | object | no | Optional capture fields supported by this daemon |
| `data.capture_fields.is_pinned` | boolean | yes | Capture accepts an initial pinned state |
| `data.capture_fields.read_later` | boolean | yes | Capture accepts an initial Read Later state |
| `data.limits` | object | yes | |
| `data.limits.request_bytes` | integer | yes | Maximum capture request bytes |
| `data.limits.title_characters` | integer | yes | Maximum title characters |
| `data.limits.notes_characters` | integer | yes | Maximum notes characters |
| `data.limits.selected_text_characters` | integer | yes | Maximum selected text characters |
| `data.limits.tag_characters` | integer | yes | Maximum characters per tag |

### BrowserIntegrationTaxonomy

Authenticated taxonomy subset used by the browser extension

| Field | Type | Required | Description |
|---|---|---:|---|
| `categories` | array<CategoryNode> | yes | Category tree |
| `tags` | array<TagWithCount> | yes | Tags |

### BrowserIntegrationTaxonomyResponse

Browser integration taxonomy response

| Field | Type | Required | Description |
|---|---|---:|---|
| `data` | BrowserIntegrationTaxonomy | yes | |
| `data.categories` | array<CategoryNode> | yes | Category tree |
| `data.tags` | array<TagWithCount> | yes | Tags |

### Diagnostics

Redacted local diagnostics payload for user-shared support bundles
Expand Down
Loading
Loading