| name | filegate-dev |
| description | Work effectively on the Filegate Go codebase itself — implementing features, fixing bugs, refactoring, writing tests. Use whenever the task touches Filegate's own production code, internal packages (domain, infra/*, adapter/http, cli, sdk/filegate), or the surrounding test/build/release infrastructure. Triggers include "filegate bug", "add a feature to filegate", "refactor the filegate index", "write a test for upload sessions", "review my filegate change". This is for working ON Filegate, not for using it from another project — for that, use the `filegate` skill instead. |
Filegate Dev
You are working on the Filegate codebase itself: a Linux-only HTTP gateway over the local filesystem with a Pebble-backed metadata index, btrfs/poll change detection, resumable upload sessions, thumbnails, EXIF, and a stable file-id model.
Filegate is treated as production infrastructure: durability and security come before clever throughput tricks, write paths must be idempotent, and every behavior change ships with a test that would have caught the previous behavior.
Workflow
- Locate the right layer. Filegate is a layered architecture (
adapter/http → domain → infra/*). Read references/module-map.md before editing — putting code in the wrong layer is the most common review-blocking mistake.
- Read the relevant convention reference. Pull only the file you need:
- Plan the change with tests in mind.
references/testing.md lists the patterns this repo uses, including the channel-based synchronization rule (no time.Sleep for sync between goroutines, ever).
- Implement. Surgical edits, no drive-by refactors of unrelated code, no speculative abstractions.
- Run the verification gates.
references/verification.md is the canonical list — go vet, staticcheck, race tests, Linux Docker tests for _linux_test.go files, fuzz smoke. Skipping any of these has historically allowed real bugs through.
- Update docs. If
api/v1/types.go, the HTTP routes, the CLI surface, or any public SDK changed, update the owning Fibel pages under docs-site/docs/en/ in the same change. The repo's own check-list (references/verification.md) calls this out.
Hard Rules — non-negotiable
These come from past incidents in this codebase, not abstract style preferences:
- Never silently overwrite or drop data on a name collision. Every
write surface that can hit a name collision (
PUT /v1/paths,
POST /v1/nodes/{id}/mkdir, POST /v1/uploads/sessions,
POST /v1/transfers) defaults to onConflict: "error". PUT /v1/nodes/{id} is exempt — it addresses an existing node by ID and is
defined as content-replacement. Adding a new write surface? If it can
hit a name collision, it must follow the same onConflict scheme —
see references/conflict-handling.md.
- Production content writes go through the blessed helpers — never direct
os.WriteFile/OpenFile. The blessed paths preserve the xattr file
ID, do symlink-rejection, and most do temp-file+atomic-rename:
writeFileAtomic (in domain/write_atomic.go) — full
temp-file+rename with parent fsync. Used by createAndWriteContent
and WriteContent.
ReplaceFile (upload session commit) — relies on Linux rename(2)
atomic-replace as the fast path, with a non-atomic OpenWrite + truncate + io.Copy fallback for cross-device cases. The fast path
is atomic; the fallback is not. If you change ReplaceFile, keep
the fast path on top.
Transfer copy and copyPath — non-atomic OpenWrite + io.Copy.
Used for cross-mount or directory-tree copies. Acceptable because
Transfer is a high-level operation already; just don't claim
atomicity for these paths.
Bypassing all of these (raw os.WriteFile) loses the xattr ID and the
stable-ID invariant goes with it.
- Bypass root/path confinement at your peril. Filegate accepts four
distinct path-input shapes; each has its own validator. Use the right
one:
- Virtual path (
mount-name/..., e.g. PUT /v1/paths): goes through
sanitizeVirtualPath → mount resolution → safeResolvedPath.
- Relative path (
mkdir body's path): goes through
sanitizeRelativePath, then walked under the parent's resolved abs.
Common pitfalls — read references/conventions.md before editing if any apply
- Adding HTTP fields without updating
api/v1/types.go AND the TS SDK in
sdk/ts/src/types.ts AND the Go SDK in sdk/filegate/ AND the
corresponding Fibel pages under docs-site/docs/en/.
- Calling
s.svc.X() from inside a long-held lock — leads to lock-ordering
deadlocks.
- Mishandling
os.ErrNotExist in rescan walks. Two classes:
(a) harmless live-race ENOENT — a file vanished between WalkDir and
our subsequent stat/setxattr; skip and continue (current rescan code
already does this, on purpose).
(b) stale-index ENOENT — the indexed path no longer exists on disk;
the detector's SyncAbsPath/RemoveAbsPath flow must clean it up.
Don't conflate them: silently swallowing class (b) leaves zombie
entries forever; treating class (a) as fatal aborts the whole rescan.
- Reusing the same
time.Sleep-based synchronization that has bitten the
repo before.
When in doubt
If the change touches Pebble layout, the index format version, or upload-session metadata schema: stop and discuss before coding. These have versioning concerns and rebuild-on-incompatible-format paths that must be updated in lockstep.