dev_plan_v0.4.md
Rama main · réplica de lectura del repositorio original.
# ServerFS MCP v0.4.0 Design Baseline — Binary Transfer & Hierarchical Workdir Policy
> Status: **RELEASED / FROZEN v0.4.0** — the tag, GitHub Release and release workflow completed successfully; post-release connector verification is recorded in [`docs/phase-f-v0.4-release-candidate-2026-09-21.md`](docs/phase-f-v0.4-release-candidate-2026-09-21.md).
>
> Base release: **v0.3.1**.
>
> This document defines the v0.4.0 contract before implementation. When v0.4 work is in
> progress, this baseline overrides historical statements in v0.1/v0.2/v0.3 plans only
> where it explicitly says so. Existing accepted v0.3 Agent Bridge contracts remain frozen
> unless this document explicitly extends their configuration inheritance.
## 1. Goal
v0.4.0 has two product themes:
1. **Binary file transfer**
- download a regular file as raw bytes through MCP;
- upload a binary payload into an explicitly writable workdir;
- explicitly overwrite an existing regular file under revision/CAS protection;
- keep binary transfer disabled by default.
2. **Hierarchical workdir policy**
- security/capability settings have a global default;
- each enabled workdir may override that default;
- workdir-specific scalar values take precedence;
- additive deny rules remain additive rather than becoming a bypass;
- effective policy is resolved once at startup and consumed uniformly by every path
channel.
The v0.4 design must preserve the defining ServerFS property:
> new capability must reuse the existing filesystem security boundary rather than create
> a second path or mutation implementation.
## 2. Non-goals
v0.4.0 does **not** add:
- a generic shell, argv executor or environment injection tool;
- recursive copy/move/rename;
- recursive delete;
- chmod/chown tools;
- arbitrary host paths;
- provider SDKs inside the MCP container;
- Internet egress from the MCP container;
- chunk/session transfer protocol unless whole-file ChatGPT E2E proves it is unavoidable;
- resumable uploads;
- background file-transfer workers;
- transfer databases;
- an OpenAI-specific HTTP downloader inside ServerFS;
- provider-specific file IDs in the public ServerFS schema.
Do not turn a bounded file-transfer feature into a general transfer service.
## 3. Frozen v0.3 security invariants
### 3.1 Filesystem traversal
Every new binary path MUST use the existing path resolver and FD-safe primitives:
```text
workdir alias
-> effective workdir policy
-> resolve_workdir_path()
-> root FD
-> dir_fd/openat walk
-> O_NOFOLLOW
-> fstat regular-file validation
```
No request-derived path may be reassembled and reopened by pathname after validation.
### 3.2 Path policy parity
Every path channel must apply the same effective policy:
- list;
- find;
- search;
- text read;
- stat;
- MCP resource;
- text mutations;
- **binary download**;
- **binary upload/overwrite**.
A binary tool is not an exception to hidden-path, deny-glob or reserved-path policy.
### 3.3 Mutation serialization
Binary upload is a mutation and therefore follows:
```text
application authorization
-> path policy
-> process-local mutation_lock
-> shared Agent writer lease
-> FD-safe atomic filesystem mutation
```
If a workspace-write Agent holds the workdir writer lease, binary upload/overwrite returns
`WORKDIR_BUSY`.
### 3.4 Network and credentials
The `serverfs-mcp` container remains:
- no Internet egress;
- no published port;
- non-root;
- read-only root filesystem;
- `cap_drop: ALL`;
- `no-new-privileges`;
- no Docker socket;
- no provider credentials.
Binary upload must not fetch a ChatGPT/OpenAI temporary download URL from inside the MCP
container. That would break the v0.3 network boundary.
### 3.5 Streamable HTTP transport security (Issue #10)
v0.4.0 closed GitHub Issue #10 before release. Docker `internal: true` networking and
an unpublished port remain defense-in-depth layers, but they must not be the only Host /
Origin / DNS-rebinding boundary for the MCP HTTP transport.
Before implementing the fix, measure rather than guess:
1. the actual `Host` header used by the current OpenAI Secure MCP Tunnel when forwarding
to `serverfs-mcp:8000`;
2. whether those requests include `Origin`, and the observed value if present;
3. the exact `mcp==2.2.0` `TransportSecuritySettings` and `streamable_http_app()` behavior
from the installed package source/runtime;
4. the narrowest existing test layer that exercises Streamable HTTP transport requests.
The implementation MUST use MCP transport-level protection with the smallest explicit
configuration that accepts the measured production Tunnel traffic and rejects unexpected
Hosts/Origins. Do not copy an Issue example allowlist or broadly allow `*` merely to make
tests pass.
Required properties:
- legal measured Tunnel Host remains accepted;
- unexpected Host is rejected before MCP request dispatch;
- disallowed Origin is rejected;
- observed legitimate no-Origin behavior, if that is what the Tunnel actually sends,
remains compatible with the pinned SDK's documented semantics;
- existing `/mcp` Streamable HTTP behavior and OpenAI Tunnel deployment remain functional;
- no new published port, reverse proxy, Internet egress or external middleware is added;
- configuration is explicit enough that changing deployment topology does not silently
widen trust.
## 4. Configuration architecture
### 4.1 Separate infrastructure settings from workdir policy
Process/infrastructure settings stay in `Settings`, for example:
- log level;
- Agent Bridge socket/timeout/lock directory;
- list/search traversal ceilings;
- transport-wide implementation limits.
Workdir-effective authorization/capability settings move into one immutable policy object.
Recommended shape:
```text
EffectiveWorkdirPolicy
read
allow_hidden
disable_default_deny
extra_deny_globs
max_read_bytes
max_read_lines
mutation
max_write_bytes
binary
binary_transfer_enabled
max_binary_transfer_bytes
agent
agent_mode
agent_runtimes
```
The exact Python representation may stay flat if that keeps the implementation smaller,
but it MUST be one immutable effective-policy object owned by `Workdir`.
### 4.2 Resolve once at startup
Environment inheritance is configuration work, not request work.
```text
.env
-> parse global defaults
-> parse WORKDIR_XX overrides
-> validate
-> build EffectiveWorkdirPolicy per enabled slot
-> immutable WorkdirRegistry
```
Tools must not re-read environment variables on each request.
### 4.3 Precedence
For scalar/boolean capability values:
```text
explicit WORKDIR_XX value
> global SERVERFS value
> safe built-in default
```
An empty workdir override means inherit.
Example:
```env
SERVERFS_BINARY_TRANSFER_ENABLED=false
WORKDIR_01_BINARY_TRANSFER_ENABLED=true
WORKDIR_02_BINARY_TRANSFER_ENABLED=
WORKDIR_03_BINARY_TRANSFER_ENABLED=false
```
Effective values:
```text
01 true
02 false (inherited)
03 false (explicit)
```
### 4.4 Additive deny rules are the deliberate exception
`EXTRA_DENY_GLOBS` is a security floor, not a normal scalar override.
Effective rules are:
```text
global SERVERFS_EXTRA_DENY_GLOBS
UNION
WORKDIR_XX_EXTRA_DENY_GLOBS
```
A workdir may tighten the global deny policy but cannot remove a global deny rule.
This is intentional even though workdir settings otherwise win.
### 4.5 Strict parsing
Security-relevant booleans must parse strictly. Unknown values abort startup.
Do not reuse the current permissive `config._get_bool()` behavior for workdir policy
switches if it would silently interpret a typo as false. The effective policy builder
must distinguish:
- empty = inherit;
- recognized true;
- recognized false;
- invalid = startup error.
Size limits must be positive integers; invalid explicit overrides must abort startup rather
than silently fall back.
## 5. Environment variable contract
### 5.1 Existing global policy defaults retained
Existing names remain valid and keep their v0.3 defaults:
```env
SERVERFS_ALLOW_HIDDEN=false
SERVERFS_DISABLE_DEFAULT_DENY=false
SERVERFS_EXTRA_DENY_GLOBS=
SERVERFS_MAX_READ_BYTES=524288
SERVERFS_MAX_READ_LINES=500
SERVERFS_MAX_WRITE_BYTES=1048576
```
Existing v0.3 deployments therefore retain their behavior.
### 5.2 New global binary defaults
```env
SERVERFS_BINARY_TRANSFER_ENABLED=false
SERVERFS_MAX_BINARY_TRANSFER_BYTES=8388608
```
Binary transfer is opt-in.
### 5.3 New global Agent policy defaults
```env
SERVERFS_AGENT_MODE=disabled
SERVERFS_AGENT_RUNTIMES=
```
These are policy defaults only.
`SERVERFS_AGENT_BRIDGE_ENABLED` remains the infrastructure master gate. If effective
workdir Agent policy enables delegation while Bridge infrastructure is disabled, startup
fails closed exactly as v0.3 does.
### 5.4 Workdir overrides
Each slot may define:
```env
WORKDIR_XX_ALLOW_HIDDEN=
WORKDIR_XX_DISABLE_DEFAULT_DENY=
WORKDIR_XX_EXTRA_DENY_GLOBS=
WORKDIR_XX_MAX_READ_BYTES=
WORKDIR_XX_MAX_READ_LINES=
WORKDIR_XX_MAX_WRITE_BYTES=
WORKDIR_XX_BINARY_TRANSFER_ENABLED=
WORKDIR_XX_MAX_BINARY_TRANSFER_BYTES=
WORKDIR_XX_AGENT_MODE=
WORKDIR_XX_AGENT_RUNTIMES=
```
Empty means inherit global defaults.
`WORKDIR_XX_READ_ONLY` remains a separate mandatory safe-default authorization/mount
setting because it drives both Compose bind-mount mode and application authorization.
### 5.5 Backward compatibility
A v0.3.1 `.env` with no new variables must produce the same effective behavior:
- binary disabled;
- current hidden/deny policy unchanged;
- current read/write limits unchanged;
- existing explicit `WORKDIR_XX_AGENT_MODE/RUNTIMES` unchanged;
- tool surface remains 11 or 19 according to the existing Agent deployment.
## 6. Workdir discovery
`list_workdirs` should expose capabilities that an MCP client needs in order to choose a
valid tool without trial-and-error.
Recommended additions to `WorkdirInfo`:
```text
binary_transfer: bool
agent_mode: disabled | review | workspace-write
agent_runtimes: list[str]
```
Do not expose deny patterns, container paths, host paths, UIDs or other security internals.
## 7. Binary tool surface
v0.4 adds exactly two public filesystem tools:
```text
download_binary_file
upload_binary_file
```
When binary transfer is disabled for every enabled workdir, neither binary tool is
registered.
Expected surfaces:
```text
filesystem only 11
filesystem + binary 13
filesystem + Agent 19
filesystem + binary + Agent 21
```
The tools may be globally visible when at least one workdir enables binary transfer;
authorization is still checked against the selected workdir on every call.
## 8. Binary download contract
### 8.1 Semantics
`download_binary_file(workdir, path)` returns the exact contents of one regular file.
It may return a PNG, ZIP, PDF, FSA, BAM, UTF-8 text file or any other regular-file bytes.
It does not need to classify the input as "binary" first. It is the raw-byte channel.
`read_text_file` remains the semantic UTF-8 text channel.
### 8.2 MCP representation
Prefer MCP-native binary content/resource representation supported by the pinned Python
SDK, i.e. a base64-backed blob at protocol level rather than inventing a ServerFS URL.
The public result must include integrity/identity metadata:
```text
workdir
path
size
mime_type
sha256
revision
binary content
```
If the SDK/tool-result shape cannot directly carry the preferred binary content in a way
ChatGPT consumes, Phase E must prove the smallest compatible representation before the
release contract is frozen. Do not enable Internet egress as a workaround.
### 8.3 Consistency
Read through one held FD:
```text
fstat/revision before
-> bounded read
-> SHA-256
-> fstat/revision after
```
If the revision changed, return:
```text
FILE_CHANGED_DURING_READ
```
If the file exceeds the effective binary transfer limit, return:
```text
BINARY_FILE_TOO_LARGE
```
Symlink, FIFO, socket and device behavior remains the same as other regular-file channels.
### 8.4 MIME
Use a best-effort MIME type; unknown is:
```text
application/octet-stream
```
MIME is descriptive metadata, never an authorization rule.
## 9. Binary upload contract
### 9.1 Provider-neutral payload
Core ServerFS accepts the binary payload directly in the tool request, encoded in a
provider-neutral representation such as strict base64.
Do not accept a public Internet URL and fetch it from the MCP container.
Base64 decoding must reject malformed input and must perform an encoded-length precheck
before allocating a decoded payload larger than the effective limit.
### 9.2 Create
Default:
```text
overwrite=false
```
means create-only.
If anything already occupies the target path:
```text
PATH_ALREADY_EXISTS
```
Creation reuses the current same-directory temp-file + fsync + `linkat` publication
primitive. A reader observes either no file or the complete new file.
### 9.3 Controlled overwrite
```text
overwrite=true
```
means replace one existing regular file.
It MUST require:
```text
expected_revision
```
and must not mean create-or-replace.
Rules:
- missing target -> `PATH_NOT_FOUND`;
- missing expected revision -> `REVISION_REQUIRED`;
- stale revision -> `REVISION_CONFLICT`;
- directory -> `NOT_A_FILE`;
- symlink -> reject through existing path/FD security;
- multiple hard links -> `MULTIPLE_HARDLINKS_NOT_SUPPORTED`.
This is the explicit v0.4 exception to the historical v0.2 "no overwrite" statement.
There is still no unguarded `force` operation.
### 9.4 Atomic replacement and metadata
Do not create a second binary mutation engine.
Refactor the existing bytes-oriented replacement primitive currently used by
`edit_text_file` so text editing and binary overwrite share:
```text
same-directory temp file
-> write bytes
-> preserve ownership/mode/xattrs
-> fsync temp
-> last-moment revision recheck
-> atomic os.replace
-> compute revision after commit
-> directory fsync/logging
```
Metadata preservation failures occur before publication.
The v0.3 hard-link restriction remains.
### 9.5 Result
Upload should report enough information for round-trip verification:
```text
workdir
path
created/replaced
bytes_written
sha256
revision
revision_before (overwrite only)
```
Do not return the payload in the mutation result.
## 10. Client-side download name collisions
ServerFS controls writes **inside configured server workdirs**.
When ChatGPT/browser saves a downloaded file to the user's local device and a local file
with the same name already exists, rename/replace behavior belongs to the client/UI and is
outside the ServerFS server contract.
The server-side "overwrite" contract applies only to `upload_binary_file` targeting an
existing ServerFS file.
## 11. ChatGPT file-input constraint
ChatGPT may expose file parameters to MCP integrations using provider-specific metadata
such as a temporary `download_url` / `file_id`. Those values are not raw bytes.
The core v0.4 ServerFS design remains provider-neutral and no-egress:
- do not put OpenAI credentials in ServerFS;
- do not fetch the temporary URL from the MCP container;
- do not add a relay service preemptively.
Phase E must run a real ChatGPT spike:
```text
ChatGPT attachment
-> ServerFS upload_binary_file
```
If the platform can deliver/directly encode data into the tool invocation, keep the core
design only. If it cannot, design the smallest **client-side** transfer helper/UI in a
follow-up change; do not weaken the server network boundary.
## 12. Whole-file transfer first
v0.4.0 intentionally does not implement:
```text
begin_upload
upload_chunk
finish_upload
transfer_id
resume_upload
orphan cleanup
background transfer workers
```
Default binary transfer ceiling:
```text
8 MiB
```
This is a bounded whole-file MCP feature. If real workloads later demonstrate a need for
large genomic binaries or other large artifacts, a chunked protocol belongs in a later
design with its own threat/concurrency model.
## 13. Recommended module boundaries
### 13.1 `config.py`
Keep process/infrastructure settings and global policy defaults.
Do not grow request-time environment lookups.
### 13.2 `workdirs.py` / optional `workdir_policy.py`
Own:
- strict override parsing;
- global/workdir inheritance;
- additive deny union;
- effective policy construction;
- validation of Agent/binary combinations.
`main.py` should stop knowing every individual `WORKDIR_XX_*` field.
Preferred startup shape:
```text
settings_from_env(env)
build_registry_from_env(env, settings)
create_server(...)
```
### 13.3 `binary.py`
If a new module is useful, keep it narrow:
- strict base64 decoding;
- bounded raw-byte read;
- SHA-256;
- binary result construction/orchestration.
Do not duplicate path traversal, mutation publication, revision or lease logic.
### 13.4 `mutations.py`
Expose reusable internal byte-payload primitives for create/replace while preserving the
existing text API semantics.
### 13.5 `tools.py`
Remain the authorization/audit/MCP registration layer.
Binary mutations use the same `_resolve_mutable`, `mutation_lock`, and
`mutation_agent_lease` sequence.
## 14. Audit logging
Binary audit records may include:
- tool name;
- workdir alias;
- relative path;
- success/error code;
- duration;
- byte count;
- overwrite boolean;
- revision;
- SHA-256 only if explicitly accepted as non-sensitive metadata.
Never log:
- binary payload/base64;
- text content;
- provider file IDs;
- temporary download URLs;
- host/container paths;
- credentials.
Prefer not to log SHA-256 unless it has a concrete operational use.
## 15. Implementation phases
### Security Track S — Streamable HTTP transport protection (Issue #10)
> Status: **IMPLEMENTED / VERIFIED** in the released v0.4.0 source. Evidence:
> [`docs/transport-security-audit-2026-09-21.md`](docs/transport-security-audit-2026-09-21.md).
>
> GitHub Issue #10 was fixed, merged and closed for the v0.4.0 release line; the
> technical implementation and real Tunnel compatibility gate are complete.
This track is release-blocking and is executed immediately after Phase A1's policy parser
foundation, before further capability expansion.
#### S1 — Runtime measurement and SDK audit
Read-only evidence only:
- capture the real Tunnel -> MCP `Host` header without changing the production route;
- determine whether `Origin` is present;
- inspect the installed `mcp==2.2.0` transport-security implementation;
- identify the existing transport-level test seam.
#### S2 — Minimal transport-security implementation
Enable MCP transport protection using the measured values and pinned-SDK behavior. Keep
configuration minimal and explicit; do not add a generic configurable wildcard allowlist
unless measurement proves it is necessary.
#### S3 — Regression and live compatibility verification
Cover at least:
- accepted legitimate Host;
- rejected unexpected Host;
- Origin allow/reject behavior;
- no-Origin behavior as actually used by the OpenAI Tunnel;
- normal MCP initialize/tools calls;
- current Tunnel readiness / real ServerFS call path after deployment acceptance.
### Phase A — Hierarchical Workdir Policy
> Status: **COMPLETE / FROZEN** on `v0.4-dev`.
>
> The effective policy is resolved once at startup, consumed by filesystem/Agent paths,
> and exposed through `list_workdirs` for client capability discovery. Phase B and later
> must reuse this policy object rather than re-read environment variables.
Implement policy inheritance first, without binary tools.
Required outcomes:
- effective immutable workdir policy;
- strict workdir override parsing;
- read visibility policy migrated to effective workdir policy;
- read/write size policy migrated where appropriate;
- Agent global defaults + workdir override;
- additive global + workdir deny globs;
- old v0.3.1 environment behavior unchanged.
Phase A MUST NOT change the 11/19 tool surface.
### Phase B — Binary Download
> Status: **COMPLETE / FROZEN** on `v0.4-dev`.
>
> `download_binary_file` is capability-gated, reuses the shared path/FD security
> boundary, returns one MCP `EmbeddedResource/BlobResourceContents` plus structured
> metadata, and was verified against the pinned `mcp==2.2.0` wire shape. Phase C must
> reuse the same effective binary policy rather than widen the download contract.
>
> Validation at freeze: 19 Phase B tests and 717 root tests passed; targeted Ruff check
> and format check passed; exact base64 round-trip and structured metadata were verified
> through an in-memory MCP tool call.
Add:
- binary capability gate;
- `download_binary_file`;
- bounded FD read;
- raw-byte result using supported MCP binary representation;
- size/MIME/SHA-256/revision;
- concurrent-change detection.
### Phase C — Binary Upload Create
> Status: **COMPLETE / FROZEN** on `v0.4-dev`.
>
> `upload_binary_file` is registered only with the binary capability surface, defaults
> to `overwrite=false`, strictly decodes provider-neutral base64 under the effective
> workdir size limit, and reuses the existing same-directory temp + `linkat` create
> primitive. `overwrite=true` is explicitly rejected until Phase D adds the
> revision-guarded replacement contract.
>
> Validation at freeze: 25 Phase C tests and 742 root tests passed; targeted Ruff check
> and format check passed; the 13-tool filesystem+binary surface, schema default,
> exact bytes/SHA-256/revision, read-only/capability/path-policy gates, symlink-parent
> rejection, Agent writer lease and no-temp-debris failure paths were verified.
Add:
- strict provider-neutral payload decoding;
- `upload_binary_file(... overwrite=false)`;
- create-only atomic publication;
- read-only and binary-capability authorization;
- Agent writer lease;
- size/hash/revision result.
### Phase D — Controlled Binary Overwrite
> Status: **COMPLETE / FROZEN** on `v0.4-dev`.
>
> `upload_binary_file(overwrite=true)` now requires `expected_revision` and reuses the
> existing `_replace_at()` atomic replacement primitive. Replacement preserves mode,
> ownership and xattrs, rejects multiple hard links, fails on stale/missing revisions,
> and never publishes a partial file. `overwrite=false` remains create-only and rejects
> an irrelevant `expected_revision` to keep the call contract unambiguous.
>
> Validation at freeze: 12 Phase D overwrite tests, 25 upload regression tests and
> 754 root tests passed with 0 skips; targeted Ruff/format checks passed; schema/default
> and error contracts were independently verified.
Add:
- `overwrite=true`;
- mandatory `expected_revision`;
- shared atomic byte replacement primitive;
- metadata preservation;
- hard-link restriction;
- revision conflict tests.
### Phase E — Real MCP / ChatGPT E2E
> Status: **SERVER/TUNNEL ACCEPTED / FROZEN** in v0.4.0; refreshed-client binary calls
> and download output-schema exposure were verified after plugin refresh.
>
> Disposable real-MCP acceptance verified 11 / 13 / 19 / 21 tool surfaces, PNG and ZIP exact-byte upload/download round trips, revision-guarded overwrite, and both directions of global/workdir binary precedence. A separate 21-tool v0.4 stack connected through the real OpenAI Secure MCP Tunnel and received four real ChatGPT `list_workdirs` dispatches with HTTP 200 and no 421/403/Host/Origin/DNS-rebinding/session errors. See `docs/phase-e-v0.4-acceptance-2026-09-21.md`.
>
> The pre-release ChatGPT conversation retained its previously discovered 19-tool schema; after the plugin refresh, direct binary upload/download calls were verified and the download `outputSchema` was exposed. That measured client-discovery limitation is historical evidence, not a remaining release gate.
Validate through the real chain:
```text
ChatGPT -> OpenAI Tunnel -> ServerFS MCP
```
Required matrix:
- binary globally off;
- binary globally on;
- per-workdir enable override;
- per-workdir disable override;
- Agent off/on combinations;
- 11/13/19/21 tool surfaces;
- real small PNG/ZIP upload and download;
- SHA-256 byte equality.
The ChatGPT attachment input shape must be measured, not assumed.
### Phase F — Security & Release Regression
> Status: **COMPLETE / FROZEN** for the released v0.4.0 source.
>
> Final gate: root `754 passed`; Agent Bridge `83 passed`; root/bridge sync, lock, Ruff and format gates all passed; base and Agent Compose renders passed; tracked deployment shell syntax passed; scratch image build passed; no-Internet-egress / no-provider-credentials / no-published-port / internal-network boundaries passed; transport-security targeted suite `47 passed`; migration/surface suite `242 passed`; full capability surface remained exactly 21 tools with no generic executor. Production identity/start/restart/health was unchanged throughout. The v0.4.0 tag/release workflow and refreshed ChatGPT connector binary upload/download verification also passed; the download `outputSchema` was exposed. See `docs/phase-f-v0.4-release-candidate-2026-09-21.md`.
Reverify:
- no Internet egress;
- no provider credentials in MCP;
- deny/hidden parity on binary channels;
- FD traversal/symlink rejection;
- writer lease;
- existing Agent approval/question/continuation/steer/cancel behavior;
- v0.3.1 upgrade compatibility;
- Compose/base/Agent overlays;
- release documentation.
## 16. Test matrix
At minimum add MCP-surface regression coverage for:
### Policy inheritance
- global false + empty override;
- global false + explicit true;
- global true + explicit false;
- invalid boolean -> startup error;
- invalid positive integer -> startup error;
- global deny + workdir deny union;
- legacy explicit Agent workdir fields;
- global Agent defaults inherited;
- explicit workdir Agent disable over global enable.
### Binary download
- PNG;
- ZIP;
- PDF;
- empty file;
- UTF-8 text through raw channel;
- hidden allowed/denied;
- global/workdir deny;
- reserved path;
- symlink final;
- symlink parent;
- FIFO/socket;
- oversize;
- concurrent change;
- binary disabled workdir.
### Binary upload create
- arbitrary bytes including NUL;
- empty payload;
- malformed base64;
- encoded-size precheck;
- oversize decoded payload;
- read-only workdir;
- binary-disabled workdir;
- denied/hidden/reserved path;
- missing parent;
- existing file/directory/symlink;
- Agent writer lease busy;
- exact SHA-256 equality;
- no temp debris after failure.
### Binary overwrite
- correct revision;
- stale revision;
- missing revision;
- missing target;
- directory/symlink;
- multiple hard links;
- mode preservation;
- ownership preservation where test environment permits;
- xattr preservation where filesystem supports it;
- complete old-or-new visibility;
- post-commit revision matches next stat.
## 17. Tool registration rules
Binary tool registration:
```text
register if ANY enabled workdir has binary_transfer_enabled=true
```
Per-call authorization still checks selected workdir.
Agent tool registration continues to depend on:
```text
SERVERFS_AGENT_BRIDGE_ENABLED
AND
any effective workdir Agent policy enabled
```
Expected public surfaces:
```text
11 filesystem
13 filesystem + binary
19 filesystem + Agent
21 filesystem + binary + Agent
```
## 18. Migration gate
Using an unchanged v0.3.1 deployment configuration with v0.4 code MUST:
- start successfully;
- retain the same effective hidden/deny/read/write policy;
- keep binary disabled;
- preserve Agent behavior;
- expose the same 11- or 19-tool surface;
- not require a new Compose overlay;
- not require a network change;
- not require a provider reconfiguration.
## 19. Release gate for v0.4.0
The v0.4.0 release gate completed as follows:
```text
[x] GitHub Issue #10 technical fix is implemented with explicit transport-security regressions
[x] GitHub Issue #10 is closed when the fix reaches the v0.4 release line
[x] actual OpenAI Tunnel Host and Origin behavior is measured and documented
[x] legitimate measured Host is accepted
[x] unexpected Host is rejected at the MCP transport layer
[x] Origin validation is covered without breaking legitimate Tunnel traffic
[x] v0.3.1 filesystem and Agent regression suites remain green
[x] effective workdir policy is resolved once at startup
[x] scalar global -> workdir precedence is fully tested
[x] additive deny union is fully tested
[x] legacy .env behavior is preserved
[x] Agent policy supports global default + workdir override
[x] binary transfer defaults disabled
[x] binary tools are absent when disabled everywhere
[x] binary tools appear when at least one workdir enables binary
[x] list_workdirs reports binary and Agent capabilities
[x] binary download returns exact bytes
[x] binary download detects concurrent changes
[x] binary download enforces effective size limit
[x] binary upload accepts arbitrary bytes including NUL
[x] malformed/oversized payloads fail closed
[x] upload create is atomic and never overwrites
[x] overwrite is explicit
[x] overwrite requires expected_revision
[x] overwrite stale revision fails
[x] overwrite preserves metadata
[x] overwrite retains hard-link restriction
[x] binary reads obey hidden/deny/reserved policy
[x] binary writes obey hidden/deny/reserved policy
[x] binary writes obey READ_ONLY authorization
[x] binary writes obey shared Agent writer lease
[x] symlinks are never followed
[x] failed binary mutations leave no published partial file/temp debris
[x] MCP container still has no Internet egress
[x] provider credentials remain host-side
[x] no generic executor exists
[x] 11/13/19/21 tool-surface matrix passes
[x] refreshed ChatGPT connector directly invokes binary upload after final edge deployment
[x] refreshed ChatGPT connector directly invokes binary download after final edge deployment
[x] current ChatGPT client tool-discovery cache limitation is measured and documented
[x] root release gate passes
[x] Agent Bridge independent gate passes when touched
[x] deployment shell syntax gate passes when touched
```
## 20. Development discipline
Work phase-by-phase. Each implementation task should be atomic and must:
1. cite the relevant section of this plan;
2. change only the minimum modules needed for that phase/sub-phase;
3. add regression tests through the public MCP surface where observable;
4. run targeted tests;
5. report exact results and residual limitations;
6. not silently continue into the next phase.
The maintainer performs architecture review between phases.
The v0.4.0 tag and release were created after Phase F closed the release gate; the
published acceptance record remains the historical evidence for that decision.