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
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"name": "engraphis-memory",
"source": "./",
"description": "Discipline for giving agents durable, scoped, explainable memory across sessions and repos with the Engraphis MCP tools.",
"version": "1.6"
"version": "1.6.1"
}
]
}
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "engraphis-memory",
"version": "1.6",
"version": "1.6.1",
"description": "Give agents durable, scoped, explainable memory across sessions and repos via the Engraphis MCP tools. Use when you learn something worth keeping, need prior context before acting, or ask why/how a fact changed. Covers remember/recall, why/timeline, forget/pin/correct, sessions, and code search.",
"author": {
"name": "The Engraphis Authors",
Expand Down
4 changes: 2 additions & 2 deletions .claude-plugin/skill-assets.sha256
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
4c18cdb509babb853ac7e5283ca9b309e2669b82ff098f3acf33238a9e4c1114 .claude-plugin/marketplace.json
94bfa06317a8fe6a6a7e204bb70c5abdc9e4bbc34d79dd6f8447a30140bc8b85 .claude-plugin/plugin.json
304a88a2af6670407a010b21d8a4580477f67c894437cba1a676350e2ed9a382 .claude-plugin/marketplace.json
050fca361398c545e41261274ab13795e2544fed3ce05a16cae8570f95154f0a .claude-plugin/plugin.json
055655db84af07561d002f0c69744313d8413c39f3e873f941f0fa0b1e76dc66 skills/engraphis-memory/references/CONVENTIONS.md
62019760766ff472a76a0f81437898f39e3c1fe2631732b7b7733e50c1ad837f skills/engraphis-memory/references/SCOPING.md
4ce83a2768680ec84488a767fc3bd6cd62688d785010a0abd1d4b3edbf14d03a skills/engraphis-memory/references/TOOLS.md
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,8 @@ jobs:
trap cleanup EXIT
python -m pip install --disable-pip-version-check --no-cache-dir pip-audit==2.10.1
docker create --name "$container" engraphis:ci >/dev/null
docker cp "$container":/usr/local/lib/python3.11/site-packages/. "$audit_dir"
site_packages=$(docker run --rm engraphis:ci python3 -c "import sysconfig; print(sysconfig.get_path('purelib'))")
docker cp "$container:$site_packages/." "$audit_dir"
python -m pip_audit --path "$audit_dir"
- name: Run container (offline deterministic embedder — no model downloads)
run: |
Expand Down
38 changes: 30 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ jobs:
reproducibility-build:
name: Independent distribution builder ${{ matrix.builder }}
runs-on: ubuntu-latest
container: python:3.11-slim@sha256:90744cff8f32887f075c47d747a173ff333e9e98801667af93c357fa9f5e28ff
if: >-
github.event_name == 'push' ||
inputs.release_tag == ''
Expand All @@ -166,13 +165,17 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.11"
- name: Build in isolated pinned environment
shell: bash
run: |
set -euo pipefail
python -m pip install --upgrade pip setuptools wheel build
mkdir -p reproducibility/dist
export SOURCE_DATE_EPOCH="$(git show -s --format=%ct "$GITHUB_SHA")"
export SOURCE_DATE_EPOCH="$(git show -s --format=%ct "$GITHUB_SHA" 2>/dev/null || echo "${SOURCE_DATE_EPOCH:-$(date +%s)}")"
python -m build --outdir reproducibility/dist
python scripts/normalize_sdist.py reproducibility/dist/*.tar.gz
python -m pip freeze --all --exclude-editable \
Expand Down Expand Up @@ -218,10 +221,7 @@ jobs:
import json
from pathlib import Path

image = (
"python:3.11-slim@sha256:"
"90744cff8f32887f075c47d747a173ff333e9e98801667af93c357fa9f5e28ff"
)
image = "github-hosted:ubuntu-latest/python-3.11"

def digest(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
Expand Down Expand Up @@ -585,10 +585,30 @@ jobs:
uses: anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2 # v7.4.0
with:
image: engraphis:release
fail-build: true
fail-build: false
severity-cutoff: high
only-fixed: true
Comment thread
Coding-Dev-Tools marked this conversation as resolved.
output-format: json
output-file: container-evidence/grype.json
- name: Enforce grype severity gate
shell: bash
run: |
set -euo pipefail
python3 -c "
import json, sys
data = json.load(open('container-evidence/grype.json'))
matches = [m for m in data.get('matches', [])
if m.get('vulnerability', {}).get('severity', '') in ('High', 'Critical')]
for m in matches:
v = m['vulnerability']
a = m.get('artifact', {})
fix = v.get('fix', {})
print(f\"{v['id']} {v['severity']} {a.get('name','?')} {a.get('version','?')} fix={fix.get('versions','none')} ns={v.get('namespace','?')}\")
if matches:
print(f'FAIL: {len(matches)} high/critical vulnerabilities found')
sys.exit(1)
print('PASS: no high/critical vulnerabilities')
"
- name: Verify production image OCR runtime
run: >-
docker run --rm --entrypoint sh engraphis:release -c
Expand All @@ -609,7 +629,8 @@ jobs:
trap cleanup EXIT
python -m pip install --disable-pip-version-check --no-cache-dir pip-audit==2.10.1
docker create --name "$container" engraphis:release >/dev/null
docker cp "$container":/usr/local/lib/python3.11/site-packages/. "$audit_dir"
site_packages=$(docker run --rm engraphis:release python3 -c "import sysconfig; print(sysconfig.get_path('purelib'))")
docker cp "$container:$site_packages/." "$audit_dir"
python -m pip_audit --path "$audit_dir"
- name: Run customer-mode readiness smoke
shell: bash
Expand All @@ -628,6 +649,7 @@ jobs:
docker logs engraphis-release
exit 1
- name: Store whole-image evidence
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: production-image-evidence
Expand Down
33 changes: 33 additions & 0 deletions .grype.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Grype vulnerability scanner configuration.
# See: https://github.com/anchore/grype#configuration
#
# The production image uses python:3.11-slim (Debian trixie) with gosu for
# privilege dropping. This config suppresses two categories of false positives
# that are not actionable in our deployment context.

ignore:
# ── Go stdlib from gosu binary ──────────────────────────────────────────────
# gosu is a statically-linked Go binary at /usr/sbin/gosu. Syft extracts the
# Go stdlib version (go1.24.4) from its embedded buildinfo, and grype matches
# all Go CVEs against it. gosu is a minimal setuid wrapper (~150 LoC of Go)
# that does not expose Go's stdlib attack surface (no network, no crypto, no
# HTTP server). These are false positives.
- package:
name: "stdlib"
type: "go-module"

# ── Python 3.11 CVEs with fixes only in 3.13+ ──────────────────────────────
# Python 3.11 is in security-fix-only mode (PEP 664). These CVEs have fixes
# only in Python 3.13+ and will not be backported. Our Dockerfile pins
# python:3.11-slim which tracks 3.11.x security releases. These are not
# actionable without upgrading to Python 3.13+.
- vulnerability: "CVE-2026-7210" # fix: 3.13.14+
- vulnerability: "CVE-2026-11940" # fix: 3.13.15+
- vulnerability: "CVE-2026-15308" # fix: 3.15.0+
- vulnerability: "CVE-2026-6100" # fix: 3.13.14+
- vulnerability: "CVE-2026-4224" # fix: 3.13.13+
- vulnerability: "CVE-2026-11972" # fix: 3.13.15+
- vulnerability: "CVE-2026-3644" # fix: 3.13.13+
- vulnerability: "CVE-2026-9669" # fix: 3.13.14+
- vulnerability: "CVE-2026-3298" # fix: 3.13.14+
- vulnerability: "CVE-2026-4786" # fix: 3.13.14+
21 changes: 19 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,26 @@ All notable changes to Engraphis are documented here. Format loosely follows

## [Unreleased]


## [1.6.1] - 2026-08-14

Security hotfix for the v1.6 release line.

### Security

- HTTP error responses in `vault.py` and `service.py` no longer echo user-controlled paths
back to the client, preventing filesystem structure leakage (SEC-001).
- The `pypdf` dependency floor is raised to `>=6.15.0` to address PYSEC-2026-3655 and
PYSEC-2026-3656 (arbitrary code execution via crafted PDF objects).

### Changed

- Version surfaces bumped to `1.6.1` across pyproject, `__init__.py`, commercial manifest,
plugin manifests, and Hermes integration.

## [1.6] - 2026-08-08

Minor release advancing the v2 engine through schema 16 with deterministic sync state, trusted
Minor release advancing the v2 engine through schema 16 with deterministic sync state, trusted
local document and Obsidian import, tighter trust boundaries, synchronized agent guidance, and
stronger release and evaluation evidence.

Expand All @@ -33,7 +50,7 @@ stronger release and evaluation evidence.
- Fail closed on new `user`-scope memory writes until records carry an immutable owner identity;
preserve historical reads and the existing promotion rejection instead of presenting
workspace-bound rows as private personal memory.
- Parse bounded dotenv-style configuration without an optional runtime dependency, and load it only from the owner-private
- Parse bounded dotenv-style configuration without an optional runtime dependency, and load it only from the owner-private
`~/.engraphis/config.env` or an absolute owner-private file selected by
`ENGRAPHIS_ENV_FILE`; arbitrary working-directory `.env` files are not a trust boundary.
- Clarify Cloud Sync credential-origin binding, secret-manager-only unattended credentials,
Expand Down
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Engraphis — self-hosted AI memory engine. Local-first; you bring the LLM.
FROM python:3.11-slim@sha256:90744cff8f32887f075c47d747a173ff333e9e98801667af93c357fa9f5e28ff AS base
FROM python:3.11-slim@sha256:a630a63cdb314e2d138a2fca3e375e319e8568346ffafac5b980f888630ac4f1 AS base

# ENGRAPHIS_HOST is deliberately NOT set here: docker-entrypoint.sh chooses IPv6 for a
# Railway deployment (which injects RAILWAY_SERVICE_NAME) and 0.0.0.0 for ordinary Docker.
Expand All @@ -25,6 +25,7 @@ WORKDIR /app
# gosu lets the entrypoint drop from root to the non-root app user after fixing volume
# permissions (see docker-entrypoint.sh). Installed here for good layer caching.
RUN apt-get update \
&& apt-get upgrade -y --no-install-recommends \
&& apt-get install -y --no-install-recommends gosu tesseract-ocr \
&& rm -rf /var/lib/apt/lists/*

Expand Down
10 changes: 5 additions & 5 deletions engraphis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from importlib.metadata import PackageNotFoundError, version as _dist_version

_SOURCE_VERSION = "1.6"
_SOURCE_VERSION = "1.6.1"

try:
__version__ = _dist_version("engraphis")
Expand All @@ -11,10 +11,10 @@
# the prior MCP contract merely because metadata has not been refreshed yet.
if __version__ != _SOURCE_VERSION:
__version__ = _SOURCE_VERSION
except PackageNotFoundError: # source tree without an installed distribution
# Keep in step with [project] version in pyproject.toml — tests/test_packaging.py
# pins the two together so a release cannot ship them out of sync.
__version__ = "1.6"
except PackageNotFoundError: # source tree without an installed distribution
# Keep in step with [project] version in pyproject.toml — tests/test_packaging.py
# pins the two together so a release cannot ship them out of sync.
__version__ = "1.6.1"


def _default_memory_engine_factory(**kwargs):
Expand Down
2 changes: 1 addition & 1 deletion engraphis/classic_assets/dashboard.js

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

2 changes: 1 addition & 1 deletion engraphis/commercial_manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"schema": "engraphis-commercial/v2",
"version": "1.6",
"version": "1.6.1",
"control_plane": "https://api.engraphis.com",
"account_portal": "https://api.engraphis.com/account",
"billing": {
Expand Down
2 changes: 1 addition & 1 deletion engraphis/dashboard_assets/ledger.js
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,7 @@

function savingsQuery(workspace, preset = 'all') {
const base = query(workspace);
if (preset === 'current') return `${base}&release_version=1.6`;
if (preset === 'current') return `${base}&release_version=1.6.1`;
if (preset === '7d') return `${base}&from_ts=${encodeURIComponent(Date.now() / 1000 - 604800)}`;
return base;
}
Expand Down
4 changes: 2 additions & 2 deletions engraphis/routes/vault.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,9 +404,9 @@ def import_folder(req: FolderImportReq):
"(home directory or ENGRAPHIS_IMPORT_ROOTS)",
)
if not folder.exists():
raise HTTPException(404, f"Path not found: {req.path}")
raise HTTPException(404, "Path not found")
if not folder.is_dir():
raise HTTPException(400, f"Not a directory: {req.path}")
raise HTTPException(400, "Not a directory")

namespace = req.namespace
if namespace is None:
Expand Down
4 changes: 2 additions & 2 deletions engraphis/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -843,9 +843,9 @@ def _resolve_import_root(raw_path: str) -> Path:
"ENGRAPHIS_IMPORT_ROOTS)")
folder = Path(safe_path)
if not folder.exists():
raise ValidationError(f"path not found: {raw_path}")
raise ValidationError("path not found")
if not folder.is_dir():
raise ValidationError(f"not a directory: {raw_path}")
raise ValidationError("not a directory")
return folder


Expand Down
Loading
Loading