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.0.0"
"version": "1.0.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.0.0",
"version": "1.0.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 @@
f4cb86b658832adc9c2d71b449091fbfcb327570cce207ab071d500029c79ac0 .claude-plugin/marketplace.json
9d5e58b9e2692e30d29ccbadbf865b30a6db72f3ff209694518c848a2b1b1e51 .claude-plugin/plugin.json
5c5336f5cc0e46c118dd13aace779555c07df1d5ec89828c02b1763a39bb637b .claude-plugin/marketplace.json
5dcc1fed4a7f2a527b1f756b90dc12d9b80fe393df0090e22a603f71c1b4faba .claude-plugin/plugin.json
89bf2728e44a3c877e31982d387fcfab10fbe065eb6441b2792375ca5105c07c skills/engraphis-memory/SKILL.md
a295b0448e2ff372ddd8ea4e0bc8dc53f3d4bd56ff1fbd0d88cf4b6179511ce1 skills/engraphis-memory/references/CONVENTIONS.md
45f4b4ad9dbfd39f2b377083d9b3eec5eed7cba7cb8fa139e3f420bdd6105343 skills/engraphis-memory/references/SCOPING.md
Expand Down
5 changes: 0 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,6 @@ ENGRAPHIS_LLM_API_KEY=sk-your-key-here
# ENGRAPHIS_CLOUD_TOKEN_SUBJECT=member
# ENGRAPHIS_CLOUD_ACCESS_TOKEN=

# Managed compute is explicit opt-in. When enabled, the client sends a bounded workspace
# snapshot capped at 16 MiB; secret-class and session-scoped rows are excluded before
# serialization, and secret-class rows are rejected by the service.
ENGRAPHIS_MANAGED_COMPUTE_CONSENT=0

# Persistent customer state (cloud session and optional saved scoped relay bearer).
# Locally defaults to ~/.engraphis; in a container use a private persistent volume.
# ENGRAPHIS_STATE_DIR=/data/.engraphis
Expand Down
4 changes: 2 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ updates:
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
open-pull-requests-limit: 5
labels:
- "dependencies"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
open-pull-requests-limit: 3
31 changes: 31 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: CodeQL

on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: "23 4 * * 1"

permissions:
contents: read
security-events: write

jobs:
analyze:
name: Analyze ${{ matrix.language }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: ["python", "javascript-typescript"]
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- name: Initialize CodeQL
uses: github/codeql-action/init@3b0bd1d116c0bde30213346b22d4f634d96a2fb0 # v3
with:
languages: ${{ matrix.language }}
build-mode: none
- name: Analyze
uses: github/codeql-action/analyze@3b0bd1d116c0bde30213346b22d4f634d96a2fb0 # v3
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ python -m scripts.consolidate --db engraphis.db --workspace acme --dry-run

# ── Sync (local shared-folder transport or hosted Cloud Sync — see docs/SYNC.md) ──
python -m scripts.sync --db engraphis.db --workspace acme --remote ~/Dropbox/engraphis --dry-run
python -m scripts.sync --db engraphis.db --workspace acme --relay https://team.engraphis.com # or bare --relay + ENGRAPHIS_RELAY_URL
python -m scripts.sync --db engraphis.db --workspace acme --relay https://relay.engraphis.com # or bare --relay + ENGRAPHIS_RELAY_URL

# ── Run the v1 server (needs the full install) ───────────────────────────────
python -m scripts.start_server # http://127.0.0.1:8700 (dashboard at /, schema at /openapi.json)
Expand Down
19 changes: 17 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,21 @@ All notable changes to Engraphis are documented here. Format loosely follows
- Graph GET requests are strictly read-only. While an explicit mutating index job runs, graph
reads return a rebuilding conflict instead of mixing old and partially derived metrics.

## [1.0.1] - 2026-07-23

Public 1.0.1 client reliability release.

### Fixed

- Cloud Sync now defaults to `https://relay.engraphis.com` and safely migrates the former
dashboard host and retired Railway relay URL without changing customer-provided relay URLs.
- Hosted Automation and maintenance requests now use the selected workspace end to end rather
than silently falling back to the first workspace.
- The Automation tab has one proposal action, clear managed-upload disclosure, and automatic
entitlement-gated managed-compute availability while preserving snapshot redaction and limits.
- Commercial metadata now describes Pro as one owner account across that owner's local
installations, matching the hosted entitlement model; Team remains billed per named seat.

## [1.0.0] - 2026-07-23

Public 1.0.0 open-core GA release.
Expand Down Expand Up @@ -99,8 +114,8 @@ Public 1.0.0 open-core GA release.
implementations from the public source and package artifacts.
- Bare memory IDs, shared-workspace controls, graph entities, statistics, snapshots, exports,
audit rows, and keyword fallbacks cannot cross authenticated session or workspace boundaries.
- Managed uploads require explicit consent, are capped at 16 MiB and 100,000 rows, omit all
session-scoped and secret-class memories, and surface only fixed client-safe provider errors.
- Managed uploads are capped at 16 MiB and 100,000 rows, omit all session-scoped and
secret-class memories, and surface only fixed client-safe provider errors.
- Customer credentials remain owner-only, redirect-safe, serialized during rotation, and are
never substituted with an unproven local machine identifier.

Expand Down
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -601,15 +601,15 @@ Manual consolidation is free and remains local. Use the dashboard's **Consolidat
`python -m scripts.consolidate`. Dry-run is the default.

Pro and Team add **hosted** Auto Consolidation and Auto Dreaming. The public Automation tab is a
policy/status client: it submits an explicitly consented, bounded snapshot to private managed
compute and displays reviewable jobs or proposals. The scheduling, analytics, dreaming, and
policy/status client: it submits a bounded snapshot to private managed compute and displays
reviewable jobs or proposals. The scheduling, analytics, dreaming, and
consolidation automation algorithms run in Engraphis Cloud; this repository ships no premium
background loop, cron wrapper, or worker.

Secret-class and session-scoped memories are excluded before a managed snapshot is serialized;
secret-class rows are rejected again by the hosted service. The encoded payload is capped at
16 MiB. Set `ENGRAPHIS_MANAGED_COMPUTE_CONSENT=1` only after reviewing that boundary. A managed
proposal does not silently rewrite the local database.
16 MiB. Managed compute is enabled automatically for authorized customers. A managed proposal
does not silently rewrite the local database.

Manual consolidation can also use schema-validated LLM output through
`MemoryService.consolidate`, `POST /api/consolidate`, `engraphis_consolidate`, or
Expand Down Expand Up @@ -656,7 +656,6 @@ All via environment (or `.env`):
| `ENGRAPHIS_CLOUD_REFRESH_CREDENTIAL` | — | Bootstrap-only rotating hosted credential; after first use the owner-only cloud session replacement takes precedence |
| `ENGRAPHIS_CLOUD_TOKEN_SUBJECT` | `member` | Subject fixed during hosted bootstrap (`device` or `member`); set explicitly with an environment-only refresh credential |
| `ENGRAPHIS_CLOUD_ACCESS_TOKEN` | — | Optional short-lived access token for ephemeral jobs |
| `ENGRAPHIS_MANAGED_COMPUTE_CONSENT` | `0` | Explicit opt-in required before uploading a bounded snapshot for hosted Analytics/Automation |

See `.env.example` for the full customer-runtime and managed-service client options.

Expand Down
6 changes: 3 additions & 3 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ them back as `expected_head` / `expected_count` when independent evidence is req
no paid-key parser, signer, issuer, local feature gate, or long-lived-key relay exchange.
- **Server authority:** every hosted and cost-bearing operation is authorized by the private
control plane; local plan labels and upgrade URLs are presentation metadata only.
- **Managed-compute consent:** Analytics, Auto Dreaming, and Auto Consolidation upload a bounded
snapshot only after `ENGRAPHIS_MANAGED_COMPUTE_CONSENT=1`. Secret-class memories are excluded
before serialization and rejected again by the hosted service.
- **Managed-compute boundaries:** Analytics, Auto Dreaming, and Auto Consolidation upload a
bounded snapshot for authorized customers. Secret-class memories are excluded before
serialization and rejected again by the hosted service.
- **Trial and grace are separate:** an email-confirmed trial lasts exactly 3 active days. A
separately bounded, maximum-24-hour local workspace-write grace never extends the trial,
subscription, Cloud Sync, managed compute, Team access, seats, or credentials.
Expand Down
4 changes: 0 additions & 4 deletions deploy/railway-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,6 @@
"ENGRAPHIS_CLOUD_COMPUTE_URL": {
"value": "https://compute.engraphis.com",
"required": false
},
"ENGRAPHIS_MANAGED_COMPUTE_CONSENT": {
"value": "0",
"required": true
}
}
}
5 changes: 2 additions & 3 deletions docs/HOSTING_RAILWAY.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,13 @@ ENGRAPHIS_CLOUD_CONTROL_URL=https://api.engraphis.com
ENGRAPHIS_CLOUD_COMPUTE_URL=https://compute.engraphis.com
ENGRAPHIS_CLOUD_ORGANIZATION_ID=org_replace_me
ENGRAPHIS_CLOUD_REFRESH_CREDENTIAL=<secret>
ENGRAPHIS_MANAGED_COMPUTE_CONSENT=0
```

Prefer mounting the owner-only cloud session file rather than placing a rotating refresh
credential directly in deployment configuration. An injected environment credential is only the
bootstrap value; after rotation, the owner-only saved replacement takes precedence. Enabling
managed-compute consent may upload a snapshot capped at 16 MiB; secret-class and session-scoped
rows are excluded client-side, and secret-class rows are rejected server-side.
managed compute for an authorized customer may upload a snapshot capped at 16 MiB; secret-class
and session-scoped rows are excluded client-side, and secret-class rows are rejected server-side.

## Persistence and recovery

Expand Down
4 changes: 2 additions & 2 deletions docs/RAILWAY_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ issuer, relay, managed compute, Auto Dreaming, Auto Consolidation, or Team ident
- No vendor signer, billing, mail, Team-admin, relay-storage, or worker secrets.

Hosted customer endpoint variables may be exposed as optional inputs, but a refresh credential
must be injected as a secret or mounted owner-only state file. Managed compute consent defaults
off.
must be injected as a secret or mounted owner-only state file. Managed compute is enabled for
authorized customers automatically.

## Publish gate

Expand Down
2 changes: 1 addition & 1 deletion docs/SYNC.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ The one-shot customer client remains available for explicit sync operations:
python -m scripts.sync \
--db engraphis.db \
--workspace acme \
--relay https://team.engraphis.com
--relay https://relay.engraphis.com
```

The dashboard's **Sync now** action invokes the same customer protocol. The public package does
Expand Down
2 changes: 1 addition & 1 deletion engraphis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
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.0.0"
__version__ = "1.0.1"
2 changes: 1 addition & 1 deletion engraphis/backends/sync_relay.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ def _safe_bundle_name(name: object) -> str:
class RelayTransport:
"""A ``SyncTransport`` backed by the customer sync relay.

``base_url`` is the relay root (e.g. ``https://team.engraphis.com``). ``workspace_id``
``base_url`` is the relay root (e.g. ``https://relay.engraphis.com``). ``workspace_id``
scopes bundles to one workspace. ``access_token`` must be a short-lived scoped cloud
bearer. The legacy-named ``license_key`` parameter is accepted only as a call-site
alias for that bearer; values with the retired ``ENGR1`` prefix are rejected. With no
Expand Down
30 changes: 11 additions & 19 deletions engraphis/cloud_features.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
"""Thin client protocol for private Engraphis managed-compute features.

The open package deliberately contains no analytics, dreaming, or automatic-consolidation
algorithm. It can prepare an explicitly consented workspace snapshot, exclude secret-classified
memories, and send that snapshot to the separately operated Engraphis Cloud service. The service
is authoritative for entitlements and performs all paid computation.
algorithm. It prepares a bounded workspace snapshot, excludes secret-classified memories, and
sends that snapshot to the separately operated Engraphis Cloud service. The service is
authoritative for entitlements and performs all paid computation.
"""
from __future__ import annotations

import json
import os
import re
import time
import urllib.error
Expand Down Expand Up @@ -42,14 +41,14 @@ def redirect_request(self, req, fp, code, msg, headers, newurl):
return None


def _truthy(name: str) -> bool:
return os.environ.get(name, "").strip().casefold() in {"1", "true", "yes", "on"}


def managed_compute_consent() -> bool:
"""Return the explicit opt-in flag; an entitlement alone is never consent."""
"""Return whether managed compute is enabled for the customer client.

return _truthy("ENGRAPHIS_MANAGED_COMPUTE_CONSENT")
Managed compute is enabled automatically for every user with a valid cloud entitlement.
This compatibility helper intentionally no longer reads an environment variable.
"""

return True


def _public_http_error(status: int) -> tuple[str, bool]:
Expand Down Expand Up @@ -184,17 +183,10 @@ def _build_managed_snapshot_locked(service: Any, workspace: str, *,
generation: Optional[int] = None) -> tuple[str, dict]:
"""Build the bounded client-side transport document for one local workspace.

Secret-classified rows are omitted before serialization. Sensitive (but not secret) content
is included only after the same explicit managed-compute consent as normal content.
Secret-classified rows are omitted before serialization. The ``consent`` parameter remains
accepted for source compatibility, but managed compute is enabled automatically.
"""

allowed = managed_compute_consent() if consent is None else bool(consent)
if not allowed:
raise CloudFeatureError(
"Managed compute is off. Opt in before uploading workspace content by setting "
"ENGRAPHIS_MANAGED_COMPUTE_CONSENT=1.",
status=409,
)
clean_workspace = service._clean_ws(workspace)
workspace_id = service._lookup_workspace(clean_workspace)
if not workspace_id:
Expand Down
4 changes: 2 additions & 2 deletions engraphis/commercial_manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"schema": "engraphis-commercial/v2",
"version": "1.0.0",
"version": "1.0.1",
"control_plane": "https://api.engraphis.com",
"managed_dashboard": "https://team.engraphis.com",
"billing": {
Expand Down Expand Up @@ -48,7 +48,7 @@
"pro": {
"monthly_usd": 10,
"annual_usd": 100,
"billing_unit": "installation",
"billing_unit": "owner",
"products": {
"monthly": {
"provider": "stripe",
Expand Down
7 changes: 4 additions & 3 deletions engraphis/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,9 +421,9 @@ def _configured_db_path(root: Path = _PROJECT_ROOT) -> str:
return str(target)


#: Vendor-hosted managed sync service. Customer deployments normally override this with
#: their own dashboard URL; local Pro clients retain the managed default.
DEFAULT_RELAY_URL = "https://team.engraphis.com"
#: Vendor-hosted managed sync service. The account dashboard remains at
#: ``team.engraphis.com``; sync traffic goes to its separate relay endpoint.
DEFAULT_RELAY_URL = "https://relay.engraphis.com"

SERVICE_MODES = ("customer",)
# The public package is a customer data plane and contains no vendor authority or hosted
Expand All @@ -435,6 +435,7 @@ def _configured_db_path(root: Path = _PROJECT_ROOT) -> str:
# managed service. Arbitrary signed URLs remain authoritative.
RETIRED_RELAY_URLS = frozenset({
"https://engraphis-production.up.railway.app",
"https://team.engraphis.com",
})

def _env(key: str, default: str = "") -> str:
Expand Down
24 changes: 16 additions & 8 deletions engraphis/routes/v2_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,16 @@ def _default_ws() -> Optional[str]:
return wss[0]["name"] if wss else None


def _require_ws() -> str:
"""Like _default_ws but raises 400 if no workspace exists yet."""
def _require_ws(workspace: Optional[str] = None) -> str:
"""Resolve an explicitly selected workspace, with a legacy default fallback.

Cloud automation is workspace-scoped. Existing clients that omit the query
parameter retain the historic first-workspace behavior, but an explicit value
is cleaned and authorized before it is used. Callers validate existence at
the cloud boundary so an unknown selection receives the documented 404.
"""
if workspace is not None:
return service()._clean_ws(workspace)
ws = _default_ws()
if not ws:
raise HTTPException(status_code=400, detail={"error": "No workspace exists yet. Create one first."})
Expand Down Expand Up @@ -1248,11 +1256,11 @@ class _AutomationReq(BaseModel):


@router.get("/automation")
def automation_get():
def automation_get(workspace: Optional[str] = None):
"""Read the cloud-authoritative managed-maintenance policy."""
from engraphis.cloud_features import CloudFeatureClient

ws = _require_ws()
ws = _require_ws(workspace)
workspace_id = service()._lookup_workspace(ws)
if not workspace_id:
raise HTTPException(status_code=404, detail={
Expand Down Expand Up @@ -1285,11 +1293,11 @@ def automation_get():


@router.post("/automation")
def automation_set(req: _AutomationReq):
def automation_set(req: _AutomationReq, workspace: Optional[str] = None):
"""Persist scheduling only in the private cloud data store."""
from engraphis.cloud_features import CloudFeatureClient, build_managed_snapshot

ws = _require_ws()
ws = _require_ws(workspace)
workspace_id = service()._lookup_workspace(ws)
if not workspace_id:
raise HTTPException(status_code=404, detail={
Expand Down Expand Up @@ -1337,11 +1345,11 @@ class _MaintenanceReq(BaseModel):


@router.post("/maintenance/run")
def maintenance_run(req: _MaintenanceReq):
def maintenance_run(req: _MaintenanceReq, workspace: Optional[str] = None):
"""Submit consolidation to managed compute; results remain generation-bound."""
from engraphis.cloud_features import run_managed_job

ws = _require_ws()
ws = _require_ws(workspace)
envelope = _managed_call(run_managed_job, service(), ws, "consolidate")
result = envelope.get("result", envelope)
return {
Expand Down
Loading