@cerefox/memory 0.11.1 → 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/cerefox.js +1077 -834
- package/dist/frontend/assets/index-CCkg5PXt.js +125 -0
- package/dist/frontend/assets/index-CCkg5PXt.js.map +1 -0
- package/dist/frontend/index.html +1 -1
- package/dist/server-assets/_shared/ef-auth/index.ts +134 -0
- package/dist/server-assets/_shared/ef-meta/index.ts +1 -1
- package/dist/server-assets/_shared/embeddings/index.ts +42 -2
- package/dist/server-assets/_shared/ingest/chunker.ts +210 -0
- package/dist/server-assets/_shared/ingest/index.ts +32 -0
- package/dist/server-assets/_shared/ingest/pipeline-helpers.ts +135 -0
- package/dist/server-assets/_shared/mcp-auth/index.ts +352 -0
- package/dist/server-assets/_shared/mcp-tools/_chunker.ts +16 -170
- package/dist/server-assets/_shared/mcp-tools/ingest.ts +13 -4
- package/dist/server-assets/db/migrations/0012_content_format.sql +20 -0
- package/dist/server-assets/db/rpcs.sql +76 -8
- package/dist/server-assets/db/schema.sql +7 -1
- package/dist/server-assets/supabase/functions/cerefox-get-audit-log/index.ts +8 -0
- package/dist/server-assets/supabase/functions/cerefox-get-document/index.ts +8 -0
- package/dist/server-assets/supabase/functions/cerefox-ingest/index.ts +22 -171
- package/dist/server-assets/supabase/functions/cerefox-list-projects/index.ts +8 -0
- package/dist/server-assets/supabase/functions/cerefox-list-versions/index.ts +8 -0
- package/dist/server-assets/supabase/functions/cerefox-mcp/index.ts +54 -0
- package/dist/server-assets/supabase/functions/cerefox-mcp/oauth.ts +121 -0
- package/dist/server-assets/supabase/functions/cerefox-metadata/index.ts +8 -0
- package/dist/server-assets/supabase/functions/cerefox-metadata-search/index.ts +8 -0
- package/dist/server-assets/supabase/functions/cerefox-search/index.ts +11 -1
- package/docs/guides/access-paths.md +81 -30
- package/docs/guides/cli.md +29 -0
- package/docs/guides/configuration.md +4 -1
- package/docs/guides/connect-agents.md +98 -54
- package/docs/guides/content-format.md +55 -0
- package/docs/guides/migration-1.0.md +87 -0
- package/docs/guides/ops-scripts.md +1 -1
- package/docs/guides/quickstart.md +20 -0
- package/docs/guides/setup-supabase.md +154 -13
- package/docs/guides/upgrading.md +4 -3
- package/package.json +1 -1
- package/dist/frontend/assets/index-ojNhWSxm.js +0 -125
- package/dist/frontend/assets/index-ojNhWSxm.js.map +0 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 2.1 protected-resource glue for cerefox-mcp (design §3–6).
|
|
3
|
+
*
|
|
4
|
+
* Pure auth logic lives in `_shared/mcp-auth/`; this file is the HTTP/Deno layer:
|
|
5
|
+
* it builds the authenticator from Function secrets, serves the RFC 9728 metadata
|
|
6
|
+
* document, and formats the 401 challenge. It is imported only by the EF.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { CORS_HEADERS } from "./shared.ts";
|
|
10
|
+
import {
|
|
11
|
+
type AuthResult,
|
|
12
|
+
createMcpAuthenticator,
|
|
13
|
+
type McpAuthenticator,
|
|
14
|
+
} from "../../../_shared/mcp-auth/index.ts";
|
|
15
|
+
|
|
16
|
+
/** Stable public path of this function (used to build absolute metadata URLs). */
|
|
17
|
+
export const FUNCTION_PATH = "/functions/v1/cerefox-mcp";
|
|
18
|
+
|
|
19
|
+
/** The protected-resource metadata route, matched as a suffix of the request path. */
|
|
20
|
+
const PRS_SUFFIX = "/.well-known/oauth-protected-resource";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Origin of the deployed project, e.g. `https://<ref>.supabase.co`, from the
|
|
24
|
+
* platform-injected `SUPABASE_URL` — NOT from request headers.
|
|
25
|
+
*
|
|
26
|
+
* SECURITY (design §6): the token issuer and JWKS URL are derived from this origin.
|
|
27
|
+
* Deriving it from client-influenced headers (`x-forwarded-host`/`-proto`) would let
|
|
28
|
+
* a caller point token validation at an attacker-controlled JWKS and forge a token
|
|
29
|
+
* that passes (JWKS-poisoning auth bypass). `SUPABASE_URL` is set by the platform for
|
|
30
|
+
* every Edge Function and is not client-controllable (every other Cerefox EF already
|
|
31
|
+
* depends on it). It is also the exact public https origin Anthropic requires for the
|
|
32
|
+
* RFC 9728 `resource` identifier — so this both hardens auth and removes the earlier
|
|
33
|
+
* `http://` (internal-proxy) scheme workaround.
|
|
34
|
+
*/
|
|
35
|
+
function projectOrigin(): string {
|
|
36
|
+
return (Deno.env.get("SUPABASE_URL") ?? "").replace(/\/+$/, "");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Absolute URL of this MCP server (the `resource` identifier — must match exactly). */
|
|
40
|
+
export function resourceUrl(): string {
|
|
41
|
+
return `${projectOrigin()}${FUNCTION_PATH}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** The Supabase auth server issuer for this project. */
|
|
45
|
+
export function issuerUrl(): string {
|
|
46
|
+
return `${projectOrigin()}/auth/v1`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** True when the request targets the (public) protected-resource metadata route. */
|
|
50
|
+
export function isProtectedResourceMetadata(req: Request): boolean {
|
|
51
|
+
const path = new URL(req.url).pathname;
|
|
52
|
+
// Accept both the plain suffix and the RFC 9728 path-insertion form
|
|
53
|
+
// (…/oauth-protected-resource/functions/v1/cerefox-mcp).
|
|
54
|
+
return path.includes(PRS_SUFFIX);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** RFC 9728 Protected Resource Metadata document. */
|
|
58
|
+
export function protectedResourceMetadata(): Response {
|
|
59
|
+
const body = {
|
|
60
|
+
resource: resourceUrl(),
|
|
61
|
+
// Anthropic reads authorization_servers[0]; Supabase serves valid AS metadata here.
|
|
62
|
+
authorization_servers: [issuerUrl()],
|
|
63
|
+
bearer_methods_supported: ["header"],
|
|
64
|
+
scopes_supported: ["openid", "email"],
|
|
65
|
+
resource_name: "Cerefox",
|
|
66
|
+
};
|
|
67
|
+
return new Response(JSON.stringify(body), {
|
|
68
|
+
status: 200,
|
|
69
|
+
headers: { ...CORS_HEADERS, "Content-Type": "application/json" },
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 401 with an explicit `resource_metadata` pointer (RFC 9728 §5.1), so clients
|
|
75
|
+
* discover our metadata route rather than probing the domain root.
|
|
76
|
+
*/
|
|
77
|
+
export function unauthorizedChallenge(result: AuthResult): Response {
|
|
78
|
+
const metadataUrl = `${resourceUrl()}${PRS_SUFFIX}`;
|
|
79
|
+
const invalid = result.ok === false && result.reason !== "no_token";
|
|
80
|
+
const params = [`resource_metadata="${metadataUrl}"`];
|
|
81
|
+
if (invalid) params.push(`error="invalid_token"`);
|
|
82
|
+
return new Response(JSON.stringify({ error: "unauthorized" }), {
|
|
83
|
+
status: 401,
|
|
84
|
+
headers: {
|
|
85
|
+
...CORS_HEADERS,
|
|
86
|
+
"Content-Type": "application/json",
|
|
87
|
+
"WWW-Authenticate": `Bearer ${params.join(", ")}`,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Build the authenticator from Function secrets + the injected SUPABASE_URL.
|
|
94
|
+
*
|
|
95
|
+
* Secrets (set via `supabase secrets set`):
|
|
96
|
+
* CEREFOX_OAUTH_OWNER_ID — pinned owner user id. The OAuth path fails CLOSED
|
|
97
|
+
* when this is unset (design §6 / Finding 3), unless
|
|
98
|
+
* CEREFOX_OAUTH_ALLOW_ANY_USER="true" is set.
|
|
99
|
+
* CEREFOX_OAUTH_ALLOW_ANY_USER — explicit opt-out of the owner pin (multi-user /
|
|
100
|
+
* sign-ups-disabled setups). Default off.
|
|
101
|
+
*
|
|
102
|
+
* The non-OAuth static path is the **Cerefox access token** (`CEREFOX_ACCESS_TOKENS`,
|
|
103
|
+
* iter-28E) — the same credential the primitive EFs accept — checked in the handler
|
|
104
|
+
* via `_shared/ef-auth` (see index.ts), NOT here. The legacy `CEREFOX_MCP_STATIC_BEARER`
|
|
105
|
+
* (anon JWT) is retired: `staticBearer` is left unset so this authenticator handles
|
|
106
|
+
* OAuth only.
|
|
107
|
+
*/
|
|
108
|
+
export function buildAuthenticator(): McpAuthenticator {
|
|
109
|
+
const issuer = issuerUrl();
|
|
110
|
+
const ownerUserId = Deno.env.get("CEREFOX_OAUTH_OWNER_ID") ?? null;
|
|
111
|
+
const allowAnyUser = Deno.env.get("CEREFOX_OAUTH_ALLOW_ANY_USER") === "true";
|
|
112
|
+
return createMcpAuthenticator({
|
|
113
|
+
issuer,
|
|
114
|
+
jwksUri: `${issuer}/.well-known/jwks.json`,
|
|
115
|
+
expectedAudience: "authenticated",
|
|
116
|
+
ownerUserId,
|
|
117
|
+
allowAnyUser,
|
|
118
|
+
staticBearer: null,
|
|
119
|
+
allowedAlgs: ["ES256", "RS256"],
|
|
120
|
+
});
|
|
121
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
|
|
2
2
|
import { createClient } from "jsr:@supabase/supabase-js@2";
|
|
3
3
|
import { isVersionRequest, versionResponse } from "../../../_shared/ef-meta/index.ts";
|
|
4
|
+
import { efAuthGate } from "../../../_shared/ef-auth/index.ts";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* cerefox-metadata — Supabase Edge Function
|
|
@@ -29,6 +30,13 @@ Deno.serve(async (req: Request): Promise<Response> => {
|
|
|
29
30
|
return new Response(null, { status: 200, headers: CORS_HEADERS });
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
const authFail = efAuthGate(
|
|
34
|
+
req.headers.get("Authorization"),
|
|
35
|
+
Deno.env.get("CEREFOX_ACCESS_TOKENS"),
|
|
36
|
+
{ ...CORS_HEADERS, "Content-Type": "application/json" },
|
|
37
|
+
);
|
|
38
|
+
if (authFail) return authFail;
|
|
39
|
+
|
|
32
40
|
if (isVersionRequest(req)) {
|
|
33
41
|
return versionResponse("cerefox-metadata", { ...CORS_HEADERS, "Content-Type": "application/json" });
|
|
34
42
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
|
|
2
2
|
import { createClient } from "jsr:@supabase/supabase-js@2";
|
|
3
3
|
import { isVersionRequest, versionResponse } from "../../../_shared/ef-meta/index.ts";
|
|
4
|
+
import { efAuthGate } from "../../../_shared/ef-auth/index.ts";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* cerefox-metadata-search -- Supabase Edge Function
|
|
@@ -43,6 +44,13 @@ Deno.serve(async (req: Request): Promise<Response> => {
|
|
|
43
44
|
return new Response(null, { status: 200, headers: CORS_HEADERS });
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
const authFail = efAuthGate(
|
|
48
|
+
req.headers.get("Authorization"),
|
|
49
|
+
Deno.env.get("CEREFOX_ACCESS_TOKENS"),
|
|
50
|
+
{ ...CORS_HEADERS, "Content-Type": "application/json" },
|
|
51
|
+
);
|
|
52
|
+
if (authFail) return authFail;
|
|
53
|
+
|
|
46
54
|
if (isVersionRequest(req)) {
|
|
47
55
|
return versionResponse("cerefox-metadata-search", { ...CORS_HEADERS, "Content-Type": "application/json" });
|
|
48
56
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
|
|
2
2
|
import { createClient } from "jsr:@supabase/supabase-js@2";
|
|
3
3
|
import { isVersionRequest, versionResponse } from "../../../_shared/ef-meta/index.ts";
|
|
4
|
+
import { efAuthGate } from "../../../_shared/ef-auth/index.ts";
|
|
5
|
+
import { capEmbeddingInput } from "../../../_shared/embeddings/index.ts";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* cerefox-search — Supabase Edge Function
|
|
@@ -68,6 +70,7 @@ const EMBEDDING_INITIAL_BACKOFF_MS = 500; // 500ms, 1s, 2s exponential backoff
|
|
|
68
70
|
|
|
69
71
|
async function getEmbedding(text: string, apiKey: string): Promise<number[]> {
|
|
70
72
|
let lastError: Error | null = null;
|
|
73
|
+
const input = capEmbeddingInput(text); // iter-28D Phase 0: cap (queries are short; safety)
|
|
71
74
|
|
|
72
75
|
for (let attempt = 0; attempt < EMBEDDING_MAX_RETRIES; attempt++) {
|
|
73
76
|
try {
|
|
@@ -79,7 +82,7 @@ async function getEmbedding(text: string, apiKey: string): Promise<number[]> {
|
|
|
79
82
|
},
|
|
80
83
|
body: JSON.stringify({
|
|
81
84
|
model: OPENAI_MODEL,
|
|
82
|
-
input
|
|
85
|
+
input,
|
|
83
86
|
dimensions: EMBEDDING_DIMENSIONS,
|
|
84
87
|
}),
|
|
85
88
|
});
|
|
@@ -182,6 +185,13 @@ Deno.serve(async (req: Request) => {
|
|
|
182
185
|
});
|
|
183
186
|
}
|
|
184
187
|
|
|
188
|
+
const authFail = efAuthGate(
|
|
189
|
+
req.headers.get("Authorization"),
|
|
190
|
+
Deno.env.get("CEREFOX_ACCESS_TOKENS"),
|
|
191
|
+
headers,
|
|
192
|
+
);
|
|
193
|
+
if (authFail) return authFail;
|
|
194
|
+
|
|
185
195
|
if (isVersionRequest(req)) {
|
|
186
196
|
return versionResponse("cerefox-search", headers);
|
|
187
197
|
}
|
|
@@ -3,27 +3,31 @@
|
|
|
3
3
|
Cerefox is built in three distinct layers. Understanding them tells you which credentials to
|
|
4
4
|
configure, what can reach the database, and which path is right for your integration.
|
|
5
5
|
|
|
6
|
-
> **A note on
|
|
7
|
-
> layers. Layer 1 (Edge Functions) uses the **
|
|
8
|
-
>
|
|
9
|
-
>
|
|
10
|
-
>
|
|
11
|
-
>
|
|
6
|
+
> **A note on credentials (2026):** Cerefox uses two different credentials for two transport
|
|
7
|
+
> layers. Layer 1 (Edge Functions) uses the **Cerefox access token** (`cfx_pat_…`) as a Bearer
|
|
8
|
+
> token — a random, Cerefox-managed secret validated in-function (the legacy Supabase anon JWT
|
|
9
|
+
> is retired for all Edge Function paths as of iter-28E). Layer 2 (web + CLI REST) uses the new
|
|
10
|
+
> **secret key** (`sb_secret_…`) or the legacy `service_role` JWT — either works. See
|
|
11
|
+
> [`setup-supabase.md` → Supabase API keys (2026)](setup-supabase.md#supabase-api-keys-2026)
|
|
12
|
+
> for the full picture.
|
|
12
13
|
|
|
13
14
|
---
|
|
14
15
|
|
|
15
16
|
## Layer 1 — AI Agents via Edge Functions (HTTPS)
|
|
16
17
|
|
|
17
18
|
This is the primary integration layer for AI clients. Nine Supabase Edge Functions are
|
|
18
|
-
deployed on the Supabase platform and are reachable over HTTPS with
|
|
19
|
-
**
|
|
20
|
-
|
|
21
|
-
internally to call Postgres RPCs.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
>
|
|
26
|
-
>
|
|
19
|
+
deployed on the Supabase platform and are reachable over HTTPS with the **Cerefox access
|
|
20
|
+
token** (`cfx_pat_…`) as a Bearer token. Each function is deployed `--no-verify-jwt` and
|
|
21
|
+
**authenticates the token in-function** (constant-time compare against the accepted set); it
|
|
22
|
+
then uses the service-role key internally to call Postgres RPCs. The token grants Edge
|
|
23
|
+
Function access only — it is never elevated to database-level access, and the service-role key
|
|
24
|
+
never leaves the server.
|
|
25
|
+
|
|
26
|
+
> ⚠️ The credential here is the **Cerefox access token**, not a Supabase key. Generate it with
|
|
27
|
+
> `cerefox token generate` (it sets the `CEREFOX_ACCESS_TOKENS` Function secret and writes
|
|
28
|
+
> `CEREFOX_ACCESS_TOKEN` to your local `.env`). The legacy Supabase anon JWT is retired for
|
|
29
|
+
> Edge Function auth as of iter-28E. See
|
|
30
|
+
> [`setup-supabase.md` → Step 7](setup-supabase.md#step-7--oauth-for-cloud-agents-claudeai--mobile-optional).
|
|
27
31
|
|
|
28
32
|
### The nine Edge Functions
|
|
29
33
|
|
|
@@ -37,7 +41,7 @@ internally to call Postgres RPCs. Your anon key is never elevated to database-le
|
|
|
37
41
|
| `cerefox-get-audit-log` | Query audit log entries with filters |
|
|
38
42
|
| `cerefox-metadata-search` | Query documents by metadata key-value criteria without text search |
|
|
39
43
|
| `cerefox-list-projects` | List all projects with names, IDs, and descriptions |
|
|
40
|
-
| `cerefox-mcp` | Streamable HTTP MCP adapter — calls Postgres RPCs directly |
|
|
44
|
+
| `cerefox-mcp` | Streamable HTTP MCP adapter — calls Postgres RPCs directly. **Also** an OAuth 2.1 protected resource for cloud/mobile Claude (optional — see "the OAuth variant" below) |
|
|
41
45
|
|
|
42
46
|
### How clients connect
|
|
43
47
|
|
|
@@ -46,10 +50,10 @@ the MCP Streamable HTTP protocol and calls the Postgres RPCs directly (no intern
|
|
|
46
50
|
to the primitive Edge Functions). The client only ever talks to one URL.
|
|
47
51
|
|
|
48
52
|
```
|
|
49
|
-
MCP client (
|
|
53
|
+
MCP client (Cerefox token)
|
|
50
54
|
│
|
|
51
55
|
▼
|
|
52
|
-
cerefox-mcp
|
|
56
|
+
cerefox-mcp (in-function token check)
|
|
53
57
|
│
|
|
54
58
|
▼ (service-role key, internal)
|
|
55
59
|
Postgres RPCs
|
|
@@ -60,15 +64,46 @@ using an OpenAPI schema. `cerefox-mcp` is not involved (ChatGPT does not support
|
|
|
60
64
|
Streamable HTTP MCP protocol).
|
|
61
65
|
|
|
62
66
|
**curl / scripts / custom HTTP clients** can also call the primitives directly using the
|
|
63
|
-
same
|
|
67
|
+
same Cerefox token as a Bearer token.
|
|
64
68
|
|
|
65
69
|
### Credentials needed
|
|
66
70
|
|
|
67
71
|
- `CEREFOX_SUPABASE_URL` — your Supabase project URL
|
|
68
|
-
- **
|
|
72
|
+
- **Cerefox access token** (`cfx_pat_…`) — generate it with `cerefox token generate`. It is a
|
|
73
|
+
random, Cerefox-managed secret (not a Supabase key), validated in-function on every Edge
|
|
74
|
+
Function call.
|
|
69
75
|
|
|
70
76
|
See `docs/guides/connect-agents.md` for step-by-step setup per client.
|
|
71
77
|
|
|
78
|
+
### The OAuth variant of `cerefox-mcp` — cloud & mobile Claude (optional)
|
|
79
|
+
|
|
80
|
+
claude.ai web and the Claude mobile app **cannot** send a static Bearer token — a custom
|
|
81
|
+
connector there requires **OAuth**. As an optional feature (iter-28A), `cerefox-mcp` is
|
|
82
|
+
therefore *also* an OAuth 2.1 protected resource, so those clients get the same full
|
|
83
|
+
tool surface. Nothing else in Layer 1 changes, and you can ignore this entirely if you
|
|
84
|
+
don't use cloud/mobile Claude.
|
|
85
|
+
|
|
86
|
+
How it differs from the static-token path:
|
|
87
|
+
|
|
88
|
+
- **Two accepted credentials.** `cerefox-mcp` authenticates each request itself
|
|
89
|
+
(`_shared/mcp-auth/`), accepting **either** a valid OAuth 2.1 access token (verified
|
|
90
|
+
against the project **JWKS**, with the token's `sub` pinned to `CEREFOX_OAUTH_OWNER_ID`)
|
|
91
|
+
**or** the static **Cerefox access token** (constant-time compare, the same token the
|
|
92
|
+
static-Bearer clients use). All nine data Edge Functions are deployed `--no-verify-jwt` and
|
|
93
|
+
do their token check in-function; `cerefox-mcp` additionally supports the OAuth arm.
|
|
94
|
+
- **Supabase is the authorization server.** The OAuth flow uses Supabase's native OAuth 2.1
|
|
95
|
+
Server. The one piece that must serve HTML — the **consent page** — is a free **Cloudflare
|
|
96
|
+
Worker** (`cloudflare/cerefox-consent/`), because a Supabase Edge Function can't serve
|
|
97
|
+
`text/html` on the default `*.supabase.co` domain.
|
|
98
|
+
- **The owner pin is the authorization boundary.** `CEREFOX_OAUTH_OWNER_ID` (the owner
|
|
99
|
+
user's UUID) is a server-side value — never entered into any client — and only tokens
|
|
100
|
+
whose `sub` matches it are accepted.
|
|
101
|
+
|
|
102
|
+
Config: `CEREFOX_OAUTH_OWNER_ID` (owner pin), a pre-registered OAuth App using
|
|
103
|
+
**`client_secret_post`**, and the Cloudflare Worker (public project URL + publishable key
|
|
104
|
+
baked in). Setup: [`setup-supabase.md` → Step 7](setup-supabase.md#step-7--oauth-for-cloud-agents-claudeai--mobile-optional).
|
|
105
|
+
Design: [`docs/specs/oauth-mcp-server-design.md`](../specs/oauth-mcp-server-design.md).
|
|
106
|
+
|
|
72
107
|
---
|
|
73
108
|
|
|
74
109
|
## Layer 2 — Web UI and CLI via Supabase REST
|
|
@@ -177,10 +212,11 @@ single container with an internally-held token.
|
|
|
177
212
|
|
|
178
213
|
| Caller | Transport | Auth credential | Typical use |
|
|
179
214
|
|---|---|---|---|
|
|
180
|
-
| Claude Code / Cursor | HTTPS → `cerefox-mcp` |
|
|
181
|
-
| Claude Desktop | HTTPS → `cerefox-mcp` (via `supergateway`) |
|
|
182
|
-
|
|
|
183
|
-
|
|
|
215
|
+
| Claude Code / Cursor | HTTPS → `cerefox-mcp` | Cerefox access token (`cfx_pat_…`) | Advanced/fallback; prefer local MCP for daily use |
|
|
216
|
+
| Claude Desktop | HTTPS → `cerefox-mcp` (via `supergateway`) | Cerefox access token (`cfx_pat_…`) | Advanced/fallback; prefer local MCP for daily use |
|
|
217
|
+
| **Cloud Claude (claude.ai web + mobile)** | HTTPS → `cerefox-mcp` over **OAuth 2.1** | Owner-pinned OAuth access token (JWKS-verified) | Optional; memory in the browser + on the phone |
|
|
218
|
+
| ChatGPT Custom GPT | HTTPS → primitive Edge Functions | Cerefox access token (`cfx_pat_…`) | AI assistant via GPT Actions |
|
|
219
|
+
| curl / HTTP scripts | HTTPS → primitive Edge Functions | Cerefox access token (`cfx_pat_…`) | Ad-hoc queries, automation |
|
|
184
220
|
| Web UI (`cerefox web`) | Supabase REST API | Secret key (or legacy service_role) | Web UI backend (TS Hono) |
|
|
185
221
|
| `cerefox` CLI (human) | Supabase REST API | Secret key (or legacy service_role) | Ingestion, search, reindex, backup |
|
|
186
222
|
| Local coding agent via `cerefox` CLI | Supabase REST API | Secret key (or legacy service_role) | User-authorised agent (Claude Code, Codex CLI, opencode, OpenClaw, Hermes, …) acting on user's behalf via Bash tool |
|
|
@@ -188,11 +224,26 @@ single container with an internally-held token.
|
|
|
188
224
|
|
|
189
225
|
### Key security principle
|
|
190
226
|
|
|
191
|
-
The (
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
227
|
+
The **Cerefox access token** (`cfx_pat_…`) is the **Edge Function credential**. It authenticates
|
|
228
|
+
to the Edge Functions (which run business logic with the service-role key internally); each
|
|
229
|
+
function validates it in-function and Supabase rate-limits the request. **It is not RLS-scoped**
|
|
230
|
+
— any holder can call the full EF tool surface (read *and* write). So treat it as a shared
|
|
231
|
+
secret for *trusted* agents/clients — keep it in local configs, but do **not** publish it on a
|
|
232
|
+
public web page. Unlike the retired legacy anon JWT (which was revoke-only on ES256-migrated
|
|
233
|
+
projects), the Cerefox token is **rotatable** with zero downtime via `cerefox token rotate`.
|
|
234
|
+
|
|
235
|
+
> **Schema 0.7.0 hardening:** the `cerefox_*` RPCs are `SECURITY DEFINER` and previously
|
|
236
|
+
> had broader-than-intended `EXECUTE` grants. They now grant `EXECUTE` only to
|
|
237
|
+
> `service_role` (which every legitimate caller uses) — revoked from `anon`/`authenticated`/
|
|
238
|
+
> `PUBLIC`. A security hardening; run `cerefox server deploy` to apply. See
|
|
239
|
+
> [`docs/specs/security-model.md`](../specs/security-model.md).
|
|
240
|
+
|
|
241
|
+
The **publishable** key (`sb_publishable_…`) is genuinely public-safe: the EF gateway
|
|
242
|
+
rejects it and (post-0.7.0) it cannot call the RPCs either, so it grants no KB access — it
|
|
243
|
+
only reaches Supabase Auth. That's why the OAuth consent page embeds it, not the anon JWT.
|
|
244
|
+
|
|
245
|
+
The secret key / `service_role` JWT and the database password must never be embedded in
|
|
246
|
+
client-facing configuration or committed to the repository.
|
|
196
247
|
|
|
197
248
|
---
|
|
198
249
|
|
|
@@ -233,7 +284,7 @@ So the access model is:
|
|
|
233
284
|
is therefore always possible until the human explicitly chooses purge.
|
|
234
285
|
|
|
235
286
|
A `cerefox purge-doc` CLI command, a `cerefox_purge_document` MCP tool, or a
|
|
236
|
-
`/documents/{id}/purge` HTTP endpoint accessible via the
|
|
287
|
+
`/documents/{id}/purge` HTTP endpoint accessible via the Cerefox token would each break this
|
|
237
288
|
property. **Do not add them without a governance design that replaces the human-in-the-
|
|
238
289
|
loop step with an equivalent guard** (e.g. a "purge approval queue" the web UI must
|
|
239
290
|
clear before the operation actually runs).
|
package/docs/guides/cli.md
CHANGED
|
@@ -539,6 +539,34 @@ Detects fresh vs. existing databases: a fresh DB gets schema + RPCs + migration
|
|
|
539
539
|
|
|
540
540
|
---
|
|
541
541
|
|
|
542
|
+
### `cerefox token generate` / `cerefox token rotate` / `cerefox token list`
|
|
543
|
+
|
|
544
|
+
**Purpose**: manage the **Cerefox access token** (`cfx_pat_…`) — the Bearer credential that
|
|
545
|
+
callers present to the Edge Functions (remote MCP, GPT Actions, direct HTTP). It replaces the
|
|
546
|
+
retired legacy anon JWT (iter-28E). The token is validated in-function; the accepted set lives
|
|
547
|
+
in the `CEREFOX_ACCESS_TOKENS` Supabase Function secret, and the value this machine presents is
|
|
548
|
+
`CEREFOX_ACCESS_TOKEN` in local `.env`.
|
|
549
|
+
|
|
550
|
+
**Synopsis**:
|
|
551
|
+
```
|
|
552
|
+
cerefox token generate # mint a token, set it on Supabase, write it to local .env
|
|
553
|
+
cerefox token rotate # add a new token (accepted set becomes [new, old]) — zero downtime
|
|
554
|
+
cerefox token rotate --finalize # drop the old token once every client is on the new one
|
|
555
|
+
cerefox token list # show masked fingerprints of the accepted set (never the value)
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
- `generate` prints the token **once** (it's a secret — reprints are impossible; lose it → `rotate`),
|
|
559
|
+
sets the `CEREFOX_ACCESS_TOKENS` Function secret, and upserts `CEREFOX_ACCESS_TOKEN` into your
|
|
560
|
+
local `.env` (backs the file up; warns if `.env` isn't gitignored; `--no-env` skips the write).
|
|
561
|
+
- `rotate` widens the accepted set to `[new, old]` so clients cut over with no downtime;
|
|
562
|
+
`rotate --finalize` removes the old token afterward.
|
|
563
|
+
- Paste the token into each client per [`connect-agents.md`](connect-agents.md): the Custom GPT's
|
|
564
|
+
Actions → Authentication (Bearer), or the remote-MCP client header.
|
|
565
|
+
- Run `token generate` **before** a token-gated `cerefox server deploy` — deploying token-gated
|
|
566
|
+
Edge Functions with no token set locks every caller out.
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
542
570
|
### `cerefox config list` / `cerefox config get` / `cerefox config set`
|
|
543
571
|
|
|
544
572
|
**Purpose**: read/write runtime config in `cerefox_config` (e.g. `usage_tracking_enabled`, `require_requestor_identity`).
|
|
@@ -607,6 +635,7 @@ These flat commands handle install, configuration, and health. Run any with `--h
|
|
|
607
635
|
| `cerefox doctor` | Diagnose the install (credentials, DB reachability, schema version). |
|
|
608
636
|
| `cerefox status` | Show connection + schema status. |
|
|
609
637
|
| `cerefox configure-agent --tool <client>` | Write MCP client config (`claude-code`, `claude-desktop`, `cursor`, `codex`, `gemini`). |
|
|
638
|
+
| `cerefox token generate` / `rotate` / `list` | Manage the Cerefox access token (`cfx_pat_…`) — the Edge Function Bearer credential (remote MCP, GPT Actions, curl). See the [`cerefox token`](#cerefox-token-generate--cerefox-token-rotate--cerefox-token-list) section above. |
|
|
610
639
|
| `cerefox self-update` | Update the installed `@cerefox/memory` package. |
|
|
611
640
|
| `cerefox completion` | Emit a shell completion script. |
|
|
612
641
|
| `cerefox backup create` / `cerefox backup restore` | File-system backup / restore of the knowledge base (see [`ops-scripts.md`](ops-scripts.md)). |
|
|
@@ -39,11 +39,14 @@ Full rule documented in [`docs/specs/polish-and-distribution-design.md` §7](../
|
|
|
39
39
|
|----------|---------|----------|-------------|
|
|
40
40
|
| `CEREFOX_SUPABASE_URL` | `""` | For app | Supabase project URL. Found in: Project Settings → API → Project URL |
|
|
41
41
|
| `CEREFOX_SUPABASE_KEY` | `""` | For app | New **secret key** (`sb_secret_…`) from Project Settings → API Keys → Secret key. Legacy `service_role` JWT also works. **Keep secret.** See [`setup-supabase.md` → Supabase API keys (2026)](setup-supabase.md#supabase-api-keys-2026). |
|
|
42
|
-
| `
|
|
42
|
+
| `CEREFOX_ACCESS_TOKEN` | `""` | For Edge Functions / e2e | **Cerefox access token** (`cfx_pat_…`), the single token this machine presents. Written to local `.env` by `cerefox token generate`. Used as the `Authorization: Bearer …` credential for remote MCP / GPT Actions / direct Edge Function calls, and read by `cerefox doctor` + the live e2e tests. Rotatable via `cerefox token rotate`. |
|
|
43
|
+
| `CEREFOX_ACCESS_TOKENS` | `""` | Server-side (Function secret) | The **server-side accepted set** — a comma-separated list of Cerefox tokens, set as a Supabase **Function secret** (not local `.env`). A request is accepted if its Bearer matches any token in the set, enabling zero-downtime rotation. Managed by `cerefox token generate` / `rotate`. |
|
|
44
|
+
| `CEREFOX_SUPABASE_ANON_KEY` | `""` | *(deprecated / unused)* | Formerly the legacy anon JWT Bearer for Edge Function calls. **Retired in iter-28E** — Edge Functions now authenticate the Cerefox access token in-function. Retained only so an old `.env` still parses; no longer read. |
|
|
43
45
|
| `CEREFOX_DATABASE_URL` | `""` | For scripts | Direct Postgres URL for deployment scripts. **Use the Session Pooler** (port `5432`) — Transaction Pooler (`6543`) does not support DDL. Username must include the project-ref suffix (`postgres.<project-ref>`). Append `?sslmode=require`. See [`setup-supabase.md` → Connection pooling (2026)](setup-supabase.md#connection-pooling-2026). |
|
|
44
46
|
|
|
45
47
|
**When each is needed:**
|
|
46
48
|
- `CEREFOX_SUPABASE_URL` + `CEREFOX_SUPABASE_KEY` — used by the CLI, web UI, and local MCP server (ingestion, search) via the Supabase Data API
|
|
49
|
+
- `CEREFOX_ACCESS_TOKEN` — used to call the Edge Functions (remote MCP, GPT Actions, direct HTTP) and by `cerefox doctor` / the live e2e tests; `CEREFOX_ACCESS_TOKENS` is its server-side counterpart (the accepted set). Generate both with `cerefox token generate`.
|
|
47
50
|
- `CEREFOX_DATABASE_URL` — used only for schema deploys (`cerefox server deploy`, or the contributor scripts `bun scripts/db_*.ts`) via a direct Postgres connection
|
|
48
51
|
|
|
49
52
|
---
|