RFC-001 · DBF Microservice Protocol v0.1

Stable, fast, interoperable access to DBF/CDX/FPT over HTTP/JSON with correct locking and clear errors.

Goal & Transport

HTTP/1.1 (or HTTP/2) JSON UTF-8 Content-Type: application/json

  • Goal. Serve DBF/CDX/FPT operations with correct file/record locking, high performance, and clear, standard errors.
  • Versioning. Prefix with /v1/…. Optional X-DBFMS-Capabilities header for features.
  • Auth. Optional bearer/API key (out of scope for v0.1).
Locking semantics: write ops perform a short FLOCK(), re-check business rule inside the lock, write, DBCOMMIT(), UNLOCK().

Conventions

  • Paths: local server filesystem (e.g., D:/DATAWIN/KUNDEN.dbf). UNC allowed but discouraged.
  • Dates: YYYY-MM-DD; hotel availability uses [start,end).
  • Strings: Server converts DBF codepage → UTF-8.
  • Numbers/Booleans: JSON native types.

Status Codes & Error Schema

200 OK
400 Bad request
404 Not found (seek/record)
409 Conflict (business rule)
412 Precondition failed (ETag)
423 Locked (file/record)
502/504 Backend down/timeout

Error body

{
  "success": false,
  "error": "E_LOCK|E_CONFLICT|E_NET|E_ARGS|E_COMMIT|E_NOREC|E_COLUMN",
  "message": "human readable",
  "details": { }
}

Minimal Endpoints (v0.1)

0) Health

GET /v1/health
200 {"ok":true,"version":"1.0.0","pid":5140,"uptime_sec":12345}

1) Read one record

POST /v1/readrecord
{"databasePath":"D:/DATAWIN/KUNDEN.dbf","recordId":23278,"fields":["NAME","PLZ","ORT"]}

200 {"success":true,"recordId":23278,"record":{"NAME":"Otto","PLZ":"9920","ORT":"Sillian"}}

2) Read range / page

POST /v1/readrange
{"databasePath":"D:/DATAWIN/KUNDEN.dbf","offset":1,"limit":50,"fields":["NAME","PLZ","ORT"]}

3) Seek (by tag/key)

POST /v1/seek
{"databasePath":"D:/DATAWIN/KUNDEN.dbf","tag":"B_NAME","key":"ATZWANGER","soft":true}

4) Update fields (atomic)

POST /v1/updaterecordfields
{
  "databasePath":"D:/DATAWIN/BELEGUNG.dbf",
  "recordId":23278,
  "fields":{"PLANNAME":"Otto","ANREISE":"2025-09-10","ABREISE":"2025-09-12"},
  "ifMatch":"\"etag-23278-abc\""
}
200 {"success":true,"updated":3,"etag":"\"etag-23278-def\""}
412 {"success":false,"error":"E_PRECONDITION","message":"ETag mismatch"}
423 {"success":false,"error":"E_LOCK","message":"file is locked"}

5) Append

POST /v1/append
{"databasePath":"D:/DATAWIN/BELEGUNG.dbf","fields":{"ZIMMERNR":"101","PLANNAME":"Muster","ANREISE":"2025-09-01","ABREISE":"2025-09-05"}}

6) Delete

POST /v1/deleterecord
{"databasePath":"D:/DATAWIN/BELEGUNG.dbf","recordId":24567}

7) Lock / Unlock (optional)

POST /v1/lock
{"databasePath":"D:/DATAWIN/BELEGUNG.dbf","type":"file"}
POST /v1/unlock
{"databasePath":"D:/DATAWIN/BELEGUNG.dbf","type":"file"}

8) Indexes & Order

GET /v1/indexes?databasePath=D:/DATAWIN/BELEGUNG.dbf
200 {"success":true,"indexes":[{"tag":"B_ANREISE","desc":false},{"tag":"B_ABREISE","desc":true}]}

POST /v1/order
{"databasePath":"D:/DATAWIN/BELEGUNG.dbf","tag":"B_ANREISE"}

9) Business hook: availability

POST /v1/isfree
{
  "databasePath":"D:/DATAWIN/BELEGUNG.dbf",
  "room":"101","start":"2025-09-10","end":"2025-09-14","ignoreBookingId":23278
}
200 {"success":true,"free":true}
409 {"success":false,"error":"E_CONFLICT","message":"overlap",
     "conflict":{"buchungsnr":99123,"anreise":"2025-09-12","abreise":"2025-09-15","planname":"Müller"}}

Logging (SHOULD)

Log one JSON line per request: timestamp, method, path, ms, bytes_in/out, pid, fileIndex (if available), databasePath, recno, clientId, result.

Implementation Notes (Server)

  • Open files locally (e.g., D:\…), not via UNC.
  • Write ops: FLOCK() → re-check → write → DBCOMMIT()UNLOCK().
  • Fallback to DBRLOCK() with short retry where file lock unavailable.
  • Return clear HTTP codes (409/423) + error JSON.
  • Expose X-DBFMS-Server: name/version header.

Client Wrapper (Harbour/Xbase++)

// Harbour/XPP helpers (v0.1)
dbf_read( cPath, nRec, aFields )        // -> hRecord | error
dbf_update( cPath, nRec, hFields )      // -> .T./.F. with error
dbf_seek( cPath, cTag, uKey, lSoft )    // -> nRec | NIL
dbf_append( cPath, hFields )            // -> nRec
dbf_isfree( cPath, cRoom, dStart, dEnd, nIgnore ) // -> .T./.F. (+conflict)

Appendix: Posting + Diagrams

Short Posting (EN)

SMB vs. Socket – why a microservice is more stable for DBF

SMB (network drives) is chatty: every seek/lock crosses the network and
deals with oplocks/leases/AV. With a socket service, one server process
opens DBFs locally (D:\...), all clients send compact requests via TCP/HTTP,
locking is clean and serialized, performance is typically 5–10× faster.

Short Posting (ES)

SMB vs. Socket – por qué un microservicio es más estable para DBF

Con SMB (unidad de red) cada operación cruza la red y negocia bloqueos.
Un microservicio en sockets abre los DBF localmente (D:\...), los clientes
envían peticiones compactas y el locking se gestiona de forma centralizada,
normalmente 5–10× más rápido.

ASCII Diagrams

SMB (Network Drive)

[Client A] -----\
                 \
[Client B] -------> [SMB Redirector] -> [Network] -> [Server FS + NTFS]
                 /
[Client C] -----/

- Each client opens DBF via SMB
- Many roundtrips per DBF op
- Locks negotiated over the network
Socket Microservice

[Client A] ----\
                \
[Client B] -------> [Socket Service @Server] -> [NTFS local]
                /
[Client C] ----/

- Only service holds DBFs locally (D:\…)
- Clients send compact requests (seek/update)
- Few roundtrips, clean locking, 5–10× faster