Commit Graph

4 Commits

Author SHA1 Message Date
Joakim Persson fdcd5871de rfc-001: resolve open questions 1 and 6 — both permissive
Recon before planning the implementation, and both blockers dissolved.

Q1, does opencode support remote MCP: yes. Its published schema defines
McpRemoteConfig (type/url/headers/oauth) as a sibling of McpLocalConfig, with
headers as a free string→string map. The RFC had been treating "every sampled
config in myconfigs is type:local" as evidence about the schema when it was
only evidence about deployments. Consequence: the edge proxy is not the only
option for opencode, so nothing in the phasing hangs on this. What opencode
still lacks is offline/local-first, which is the honest Phase 2 argument.

Q6, how opencode-devbox learns the opt-in: it already does. generate-config.py,
run from entrypoint-user.sh:117, registers mempalace as remote+bearer when
MEMPALACE_REMOTE_URL is set and local stdio otherwise, and its comment says it
deliberately mirrors the mempalace.ts env contract. The image also ships
mempalace by default, so R5's premise was wrong and is corrected. Phase 2 there
is a third branch in an existing script, not a new mechanism.

One new constraint found while confirming this, and it is the sharpest edge in
the whole opt-in story: generate-config.py never overwrites an existing config
and ~/.config/opencode is a named volume, so flipping the .env on a container
that already has a config is a no-op — it only writes an opencode.jsonc.proposed
sidecar. pi re-reads env every start; opencode does not. Documented in §4.1 and
§9.6, and it applies symmetrically to R6 reversibility.
2026-08-09 22:42:00 +02:00
Joakim Persson 052dbb8038 docs(rfc-001): provenance belongs to the sync boundary, not the agent
Reverses the previous §7.3 on review. It said "stamp added_by everywhere,
now, because it cannot be backfilled" and was about to become a skill
instruction telling agents to do it. Both halves were wrong.

Wrong on ownership: provenance answers "which device asserted this?", so
only a party that can verify the answer should write it. An agent must shell
out to read env, can forget, and will improvise when the values are absent —
the worst possible stamper, and its claim is unverifiable by anyone. Under
the §4 design every write reaches the primary over an authenticated channel,
including offline ones at outbox-flush time, so the primary can stamp the
complete set with no client cooperation. Provenance is a property of the sync
channel, not of the record's author. §7.3.2 adds the trust ladder; edge-side
stamping is demoted to an advisory interim, because 3.6.0's serve takes a
single shared bearer token (mcp_server.py:5291-5293) and the package has zero
device/origin concept, so authoritative stamping needs the per-device
credentials of §6 — Phase 4, not Phase 0.

Wrong on backfill: a solitary devbox is a single-origin store by definition,
so origin is a property of the whole palace and can be assigned wholesale at
import (one --origin-device flag) at the moment it stops being solitary. Bulk
attribution is strictly more reliable than per-record stamping since it
cannot be partially applied. Per-record provenance is only needed where
origins interleave, which is only the primary. Solitary containers therefore
stamp nothing and lose nothing — more R1-compliant than the previous draft,
which quietly asked users who had opted out to carry metadata for the
feature. Multi-harness-on-one-host stays solved by added_by = agent name.

Keeps the verified mechanics (fixed metadata schema, argument whitelisting at
mcp_server.py:4777 silently dropping unknown fields, the diary agent_name →
wing_pi@host trap, kg_add having no provenance slot, added_by absent from
search results) and the identity findings (a container cannot discover its
host's identity; hostnames are neither unique nor stable; rename splits one
device's history in two). §7.3.5 keeps the fail-closed rule for whichever
component does stamp.

Phase 0 drops its provenance item accordingly, and §6.2 now specifies
server-side stamping from the authenticated credential.

Also flags in-document that these notes are a poisoning vector: a future
agent reading them out of the palace must not conclude it should hand-stamp.
2026-08-09 15:40:25 +02:00
joakimp 661ee20b39 docs(rfc-001): require solitary-first operation, opt-in centralization
Adds §1.1 (R1–R6) and §1.2 as a hard constraint on the design rather than a
preference. A shared palace is valuable only for the multi-machine /
multi-container pattern; for most users of the published pi-devbox and
opencode-devbox images it is useless overhead. Evidence: all three sampled
opencode-devbox deployments contain zero mempalace references.

Requirements: solitary operation stays the default and stays byte-identical
(no extra process, no outbox, no network calls); opt-in via docker-compose.yml
+ .env only, never an image rebuild; credentials only in .env, never in
compose, the image, a command line, or a log; no new required services (the
primary stays a separate standalone compose project); degrade-not-fail where
mempalace is absent; fully reversible.

§1.2 extends the convention pi-devbox already ships (.env.example:12-23,
"local by default" with commented MEMPALACE_REMOTE_URL/_TOKEN) into a
three-state ladder — local stdio (default, unchanged) / direct remote
(unchanged) / edge (new, MEMPALACE_EDGE=1) — instead of inventing a new
mechanism. Notes that the devbox-palace volume coupling reverses under edge
mode: the local palace holds the outbox, so persisting it becomes required
rather than irrelevant.

Consequence recorded in §4.1: mempalace-edge must be selected at registration
time, not left always-in-path to decide by env at runtime, since that would
insert a process and a failure mode into every solitary user's setup. pi
branches in createClient(); opencode needs its static MCP JSON templated at
container start, which is new open question §9.6.

Also: adds an R1 acceptance test, marks pi-devbox/.env.example:21 as stale
(advertises mempalace-mcp --transport http rather than mempalace serve
--token/--tls-cert), and extends the evidence index.
2026-08-09 15:02:51 +02:00
joakimp 35b1e3d81d docs: add RFC 001 — global palace with local fallback (mempalace-edge)
Design for moving from one MemPalace per machine per harness to a single
primary palace with per-machine offline fallback, plus the source-verified
archaeology behind it.

Key decisions recorded:

- Sync operations (MCP tool calls), not databases. Embeddings are computed
  client-side and are not portable across architectures; the KG `triples`
  table has no UNIQUE(subject,predicate,object,valid_from) and its ids embed
  datetime.now(), so row copies duplicate facts. Replaying tool calls is
  idempotent where it matters.
- Implement as `mempalace-edge`, a local stdio MCP proxy (child mempalace-mcp
  + HTTPS to the primary + outbox.sqlite), not as per-harness patches. Needs
  zero mempalace internals, so it serves pi, opencode and the CLI alike and
  survives mempalace upgrades.
- Merged reads (query both, re-sort, dedupe by drawer id) give fleet-wide
  recall without replication — which is why Phase 3 (pull replication) is
  deferred: "own writes plus whatever it can reach" is good enough.
- Per-wing replication policy: curated/content-addressed wings replicate,
  mined code/docs stay local (derived, re-mineable, path-dependent ids).

Also documents two silently destructive footguns to avoid during rollout:
`--palace` vs MEMPALACE_PALACE_PATH (the KG follows the flag only, so
`mempalace serve` can start with a silently empty knowledge graph), and
`mempalace sync`, which is gitignore-aware drawer deletion rather than
replication and would wipe fleet memory when run from a host lacking the
repos. Notes that mempalace 3.6.0's `serve` already ships token auth + TLS,
making the "unauthenticated, front it with a proxy" notes elsewhere stale.

Includes an evidence index mapping each claim to file:line in mempalace
3.6.0, and four upstream candidates (origin_host provenance, per-wing ACL,
mempalace_kg_supersede missing from service.py WRITE_TOOLS, and a
sync --refuse-shared guard).
2026-08-09 00:30:00 +02:00