Commit Graph
11 Commits
Author SHA1 Message Date
shadowdaoandClaude Opus 5 8dff061faf chore: genericize plugin manifests for public release
main is now the shareable artifact: placeholder host and clientId, no
instance-specific hostnames, marketplace renamed to cybercove-labs.

The filled-in manifest for the live instance lives on branch
instance/dnspegasus and is installed with a #ref fragment, which
`claude plugin marketplace add` honors and persists even though it is
absent from --help (verified on Claude Code 2.1.220).

README: plugin install is now path A, documenting both the fork-and-edit
and the #ref branch approaches; remaining paths renumbered B/C/D.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 21:41:20 -07:00
shadowdaoandClaude Opus 4.7 0afa9e86ae chore: anonymize deployment URL in docs and UI
Replace hardcoded memory.dnspegasus.net references throughout README
with the generic memory.example.com placeholder (matches .env.example).

In tokens-manager.tsx, the claude-mcp-add snippet shown to users now
derives the host from PUBLIC_URL via a server-side prop instead of a
hardcoded literal, so any deployer sees their own URL in the snippet.

Prepares the repo for public mirroring.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 08:58:57 -07:00
shadowdaoandClaude Opus 4.7 d1d4c60f2d fix: address Phase-5 code review (4 findings)
Reviewer flagged two security-relevant items and two operational bugs
on the external-DB + Terraform module merge.

1. Terraform: `enable_execute_command = true` was hardcoded on the app
   and embedder services. Production attack surface (anyone with
   `ecs:ExecuteCommand` on the service gets a container shell) AND
   non-functional today since the task roles have no `ssmmessages:*`
   permission. Added a new `enable_execute_command` boolean input
   variable defaulting to `false`; when flipped on, the SSM messages
   policy is conditionally attached to both task roles so the feature
   actually works. README's variable description tells operators to
   flip on for incidents, off afterward.

2. Terraform: `secret_arns` output was not marked `sensitive`. The ARNs
   themselves aren't secrets, but the embedded secret names print to
   `terraform apply` stdout and CI logs. Marked sensitive on both the
   module output and the example output. Operators wanting the values
   can still `terraform output -json secret_arns`.

3. Terraform: embedder task definition was missing `HOST=0.0.0.0` and
   `PORT=8080`. Fargate awsvpc tasks each get their own ENI; default
   Node HTTP servers bind 127.0.0.1, which would make every
   app→embedder Service Connect call time out. Added both vars to
   `embedder_environment`. Also added `NEXT_TELEMETRY_DISABLED=1` to
   `app_environment` per the spec's hardening checklist.

4. Compose: docker-compose.external-db.yml uses the `!override` YAML
   tag, which requires Docker Compose >= 2.24.0. Silently ignored on
   older Compose, causing the `db` dependency to survive the merge and
   startup to fail. Documented the minimum version in the override
   file's header AND in the main README prerequisites with a deep
   link to the External Postgres section.

terraform fmt + validate (module + examples/basic) both clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 09:48:04 -07:00
shadowdao a92504d799 Merge: Terraform module + AWS Fargate guide (Agent B) 2026-05-18 09:41:08 -07:00
shadowdaoandClaude Opus 4.7 08be60e661 feat(terraform): AWS Fargate deployment module
Adds a terraform/ directory with an opinionated module that deploys
shared-memory to ECS Fargate behind an ALB. The module assumes the
operator already provides the VPC, RDS Postgres, ACM cert, ECR images,
and OIDC clients, and creates everything else: ECS cluster + services,
ALB, Service Connect namespace for app-embedder discovery, EFS-backed
model cache for the embedder, Secrets Manager entries, IAM roles,
CloudWatch log groups, and a one-shot migrator task definition.

Includes examples/basic/ with a worked invocation and a README covering
prerequisites, quick start, the post-apply migrator run, image updates,
DNS setup, and a security note. Main README gains a short Mode C
pointer to the terraform/ guide.

Validated with `terraform fmt -check -recursive` and
`terraform validate` against AWS provider 5.x.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 09:40:12 -07:00
shadowdaoandClaude Opus 4.7 93b127f112 feat(compose): opt-in external-Postgres override
Adds `docker-compose.external-db.yml` so teams can point the stack at a
managed Postgres (RDS, Cloud SQL, etc.) without forking the base compose
file. Disables the bundled `db` service via an unreachable `profiles`
label and replaces `depends_on` / `DATABASE_URL` on `migrator` and `app`
with `!override`-tagged blocks that read `DATABASE_URL` straight from
`.env`. Default `docker compose up -d` flow is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 09:34:23 -07:00
shadowdaoandClaude Opus 4.7 b6e28b329d feat: .shared-memory-project file convention for repo-rooted project ID
Replaces "per-machine X-Project-Key header" as the default way to tell
Claude Code which shared-memory project a repo belongs to. Commit a
single-line `.shared-memory-project` text file at the repo root; every
collaborator's Claude Code reads it at session start and attaches all
memories + snippets to the same shared project. No per-machine config
required.

Changes:
- New file convention documented in README (resolution order, format,
  authoring snippet, rationale for plain-text over JSON).
- project.identify tool description now leads with "check
  .shared-memory-project at the repo root", with inference as fallback.
- memory.write description mentions the file as the canonical source for
  project keys.
- Project detail page in the Web UI shows a copy-paste `echo > file`
  command so users see exactly what to add to their repo.
- Added .shared-memory-project to this repo (content: `shared-memory`).

Resolution precedence is: explicit tool arg → .shared-memory-project →
X-Project-Key header → inference. The file beats the header because
repo context is more specific than machine context.

This is a soft convention — Claude has to read the file. Directive tool
descriptions make this very likely; a Claude Code skill would make it
bulletproof, deferred until we see whether the description alone is
enough.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 09:17:04 -07:00
shadowdaoandClaude Opus 4.7 7712023c32 feat(phase-4a): groups sync + X-Project-Key header substrate
Foundational work for the upcoming group-scoped sharing feature.

Schema (migration 0003_groups.sql + drizzle schema):
  - memory_access enum ('ro' | 'rw') reserved for Agent B's project_shares
  - groups (id, oidc_iss, name, display_name, …) keyed by (oidc_iss, name)
    so different IdPs can both have e.g. "platform" without colliding
  - user_groups (user_id, group_id, synced_at) PK (user_id, group_id)

Auth (auth.ts + lib/auth/sync-groups.ts):
  - jwt callback now syncs `profile.groups` after upserting the user
  - syncUserGroupsFromClaim runs in a single tx: upserts each group,
    inserts new memberships, deletes ones no longer in the claim
  - missing/empty claim → user has zero groups (wipe memberships)
  - EntraID GUID-vs-name edge case: we treat whatever strings the claim
    emits as names verbatim; groups overage (>200 groups → no claim)
    is documented as unsupported in v1

UserContext + JWT (lib/mcp/context.ts, lib/auth/jwt.ts):
  - AuthenticatedClaims.groups surfaced from verified JWT payload
  - UserContext.groups: string[] — live from OIDC token claim, falls
    back to DB snapshot for CLI (HMAC) tokens which carry no claim
  - UserContext.defaultProjectKey: optional, set from header

MCP route (app/api/mcp/route.ts):
  - reads X-Project-Key header, validates against ProjectKey Zod schema,
    400 on invalid; empty/missing leaves defaultProjectKey undefined
  - auto-upserts the header-supplied project so first-use works without
    a separate project.identify call

Tools (lib/mcp/tools.ts):
  - withDefaultProject helper injects ctx.defaultProjectKey when the
    caller omits `project`. Per-tool defaultScope hint avoids breaking
    snippet.put (user-scope default) while making memory.write
    (project-scope default) honor the header
  - applied to memory.write/list/search/update and all snippet.* tools

Web UI:
  - /settings/groups debug page lists current memberships with synced_at
    and a clear empty state pointing at README troubleshooting
  - /settings/tokens grows a "Pin to project" dropdown; selected key is
    baked into the generated `claude mcp add` snippet as
    `--header "X-Project-Key: <key>"`. The JWT itself stays
    identity-only — pinning is purely a UX shortcut
  - settings landing page links to /settings/groups
  - README troubleshooting bullet covers the empty-groups path for
    Authentik / EntraID / Keycloak

Refactor:
  - extracted resolveProjectId + upsertProject from memory-actions.ts
    into lib/projects.ts so the MCP route can reuse upsertProject

Verification:
  - pnpm typecheck clean
  - SKIP_ENV_VALIDATION=true pnpm build clean; /settings/groups in route table

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 09:39:47 -07:00
shadowdaoandClaude Opus 4.7 609039f098 feat: /auth/cli-callback page + OAuth-first connect docs
Adds the manual-paste fallback page MCP clients hit when their OAuth
loopback callback isn't reachable (sealed containers, port-restricted
hosts). The page displays the authorization code, the full callback URL,
and the state parameter, all with copy buttons, plus instructions to
paste back into the waiting terminal. Single-use codes plus client-side
PKCE mean displaying the code here is safe — it isn't a credential by
itself.

README "Connecting Claude Code" rewritten to make OAuth the primary
path, with three options ordered by preference: (A) standard OAuth +
loopback, (B) manual paste via /auth/cli-callback, (C) static HMAC
bearer via /connect for fully headless setups. Also notes that the
zero-config plugin path is blocked on Authentik DCR (issue #8751,
expected later this year) so we're shipping the one-liner now.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 10:37:08 -07:00
shadowdaoandClaude Opus 4.7 d7707dd158 refactor: generic OIDC provider — Auth.js no longer Authentik-specific
Replaces the Authentik-preset provider with an inline generic OIDC config
(id: "oidc"). The verifier path was already protocol-generic; the only
Authentik-named piece was the next-auth provider preset, which mapped to
the same OIDC fields anyway. Any compliant IdP — Authentik, EntraID,
Keycloak, Okta, Auth0, Zitadel, etc. — now works with just the existing
OIDC_* env vars.

Breaking change for existing deployments: the Auth.js callback path
changes from /api/auth/callback/authentik → /api/auth/callback/oidc.
Update the Web-UI client's redirect URI in your IdP before redeploying.

README rewritten to frame Authentik as the worked example, with a
concept-mapping table for EntraID and Keycloak, and audience-claim notes
for non-Authentik IdPs in the troubleshooting section.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 10:15:44 -07:00
shadowdaoandClaude Opus 4.7 077d0a0825 feat: Phase 1 — Authentik auth, MCP endpoint, persistent memory
End-to-end Phase 1 of shared-memory: a logged-in Authentik user can sign
into the Web UI (/me debug page), and an MCP client with an Authentik-
issued bearer token can call memory.write / memory.list / memory.get /
memory.delete plus project.identify against /api/mcp.

Stack:
- Next.js 15 (App Router) + React 19 + TypeScript, pnpm workspaces
- Drizzle ORM + Postgres 16 + pgvector + pg_trgm
- Auth.js v5 with Authentik provider (Web UI)
- jose + Authentik JWKS for MCP bearer-token validation
- JSON-RPC 2.0 dispatcher implementing the MCP wire protocol over plain
  HTTP POST (hand-rolled to fit Next.js App Router; switches to SSE in a
  later phase if server-initiated events are needed)
- bge-small embeddings sidecar deferred to Phase 2; the schema already
  reserves the vector(384) column + IVFFlat index, FTS via a STORED
  tsvector column, and the visibility enum (private/shared/team) so
  cross-user memory sharing can be added without a future migration

Deployment supports two modes (set in .env, never committed):
- Behind an external reverse proxy (HAProxy / nginx / Cloudflare Tunnel /
  Traefik) — DEFAULT; the app exposes APP_PORT on the host with
  X-Forwarded-* trusted, no in-container TLS
- Built-in TLS via Caddy — opt-in with `docker compose --profile tls up`

Discovery endpoint at /.well-known/oauth-protected-resource (RFC 9728)
points MCP clients at the Authentik authorization server after a 401.

README walks through both Authentik providers (Web UI + MCP resource
server), the audience scope mapping, redirect URIs, and includes a worked
HAProxy config snippet.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 07:04:11 -07:00