The Filecoin mediator is an auxiliary storage service. It drains the
shared pin Gatekeeper queue and asks the
filecoin-wallet service to store each
queued operation CID on Filecoin via Synapse / Filecoin Pay.
Filecoin is not a canonical DID registry in Archon. Operations keep
their original registry (e.g. BTC:mainnet, ETH:sepolia, hyperswarm)
and are copied to Filecoin only for storage durability. The mediator
holds no key material; signing and payment are delegated to the wallet
service.
The canonical implementation is services/mediators/filecoin/.
Related specs. The filecoin mediator reads from the Gatekeeper
pinqueue (getQueue('pin')/clearQueue('pin', ops)/addJSON) and delegates all Filecoin-side work to the filecoin-wallet over HTTP.
A single background import loop. There is no chain scanner and no
export-to-registry loop — Filecoin is write-only storage from this
mediator’s perspective, and the pin queue is populated by Gatekeeper
when operations are accepted on any registry.
Fires every ARCHON_FIL_IMPORT_INTERVAL minutes (default 1). For
each tick:
gatekeeper.getQueue('pin') — fetch the pending operations.cipher.hashJSON(canonicalize(operation)).pinned, skip and mark the operation as completed.gatekeeper.addJSON(canonicalize(operation))) and call
wallet POST /api/v1/wallet/pin { cid, fingerprint, registry }.pinned in the local state and mark the
operation as completed.failed for that fingerprint, log the error,
stop processing further operations, and leave the rest
queued for the next tick.gatekeeper.clearQueue('pin', completed) — remove the completed
operations from the queue.The loop is single-flight (a re-entrant call is dropped) and uses a 300 s timeout on the wallet call.
When a pin failure message contains Insufficient FIL or USDFC, the
mediator calls wallet GET /api/v1/wallet/version and logs the funding
address and network so the operator knows where to top up:
Filecoin wallet needs calibration FIL. Send calibration FIL to 0x…
The mediator does not write any DIDs to the Gatekeeper. The only writes are to its own local state file. It speaks to two collaborators over HTTP:
| Call | Purpose |
|---|---|
getQueue('pin') |
List operations waiting to be pinned. |
addJSON(canonical) |
Pin the canonical operation JSON to local IPFS, return its CID. |
clearQueue('pin', ops) |
Drop completed operations from the pin queue. |
isReady() |
Used by /ready. |
| Call | Purpose |
|---|---|
POST /api/v1/wallet/pin { cid, fingerprint, registry } |
Export the IPFS DAG for cid as CAR data and upload it through Synapse. |
GET /api/v1/wallet/version |
Called on every /ready probe to determine readiness, and also used to fetch the funding address when a failure hints at insufficient funds. |
All calls include X-Archon-Admin-Key: ${ARCHON_ADMIN_API_KEY} when the
key is configured.
The fingerprint is the canonical hash of the operation JSON, not of the IPFS CID. This is what is used to deduplicate across ticks: an operation that has already been pinned on Filecoin will be skipped even if Gatekeeper re-enqueues it under a different CID encoding.
JsonPinStore)A single JSON file at ARCHON_FIL_STATE_PATH
(default ./data/filecoin-pins.json):
{
"version": 1,
"pins": {
"<fingerprint>": {
"fingerprint": "<sha256 hex of canonical operation>",
"cid": "<IPFS CID of canonical operation>",
"registry": "<original registry, e.g. BTC:mainnet>",
"status": "pinned" | "failed",
"attempts": <int>,
"created": "<RFC 3339>",
"updated": "<RFC 3339>",
"wallet": <opaque WalletPinResult JSON from filecoin-wallet>,
"lastError": "<string, set when status is failed>"
},
...
}
}
The store is loaded lazily on first import-loop tick and rewritten in full on every record update. There is no other backend (no SQLite / Redis / Mongo variant for filecoin pins).
Metrics-only, binds to ARCHON_FIL_METRICS_PORT (default 4271).
| Method | Path | Body |
|---|---|---|
GET |
/health |
{ ok: true } |
GET |
/ready |
{ ready: <bool> } — true iff Gatekeeper is ready AND wallet/version responds. |
GET |
/version |
{ version, commit } |
GET |
/metrics |
Prometheus |
No /api/v1/* routes, no admin auth, no public client surface.
waitUntilReady=true implicit in
gatekeeper.connect).importInterval > 0, run one immediate import tick, then
setInterval(importInterval * 60_000).| Variable | Default | Meaning |
|---|---|---|
ARCHON_NODE_ID |
unset | Logged at startup; not required for filecoin (no on-chain identity). |
ARCHON_ADMIN_API_KEY |
unset | Sent as X-Archon-Admin-Key to Gatekeeper and filecoin-wallet. |
ARCHON_GATEKEEPER_URL |
http://localhost:4224 |
Gatekeeper service URL. |
ARCHON_WALLET_URL |
http://localhost:4270 |
filecoin-wallet service URL. ARCHON_FIL_WALLET_URL accepted as alias. |
ARCHON_FIL_IMPORT_INTERVAL |
1 |
Minutes between import-loop ticks. 0 disables the loop (mediator becomes idle). |
ARCHON_FIL_METRICS_PORT |
4271 |
Metrics HTTP port. |
ARCHON_FIL_STATE_PATH |
./data/filecoin-pins.json |
Local state file. |
GIT_COMMIT |
unknown |
Embedded in /version and service_version_info. |
No explicit signal handlers. On SIGTERM the process exits; an in-flight pin upload will be aborted by Node’s socket teardown and re-attempted on the next tick (the wallet may still complete the underlying Synapse upload, but the local record will be re-created under the same fingerprint).
Gauges:
| Metric | Notes |
|---|---|
filecoin_mediator_queue_depth |
Operations returned by the last getQueue('pin') call. |
filecoin_mediator_pin_records{status="pinned"\|"failed"} |
Count of records in the local state file. |
filecoin_mediator_import_active |
0 / 1 — single-flight loop guard. |
Counters:
| Metric | Notes |
|---|---|
filecoin_mediator_pins_total{status="pinned"\|"failed"} |
Per-tick pin attempt outcomes. |
Plus service_version_info{version,commit} and standard Prometheus
process metrics.
Plain console.log / console.error:
empty pin queue — per tick when nothing is queued.Pinned N pin operation(s) to Filecoin — successful tick summary.Filecoin pin failed: <message>; leaving remaining operation(s) queued — failure path.Filecoin wallet needs <network> <token>. Send <network> <token> to <address> — funding hint, only when the failure message names a token.ghcr.io/archetech/filecoin-mediatorpin queue contract, but uses an IPFS Pinning Service API (Filebase,
Pinata, etc.) instead of Filecoin/Synapse. Run only one mediator per
pin queue.A conformant third implementation MUST:
pin queue exactly as in §1.1, using
clearQueue('pin', ops) only for operations it successfully pinned.