Skip to content

Add dittosh server group — Ditto Server (portal) HTTP API client - #4

Open
Aaron LaBeau (biozal) wants to merge 2 commits into
mainfrom
portal-support
Open

Add dittosh server group — Ditto Server (portal) HTTP API client#4
Aaron LaBeau (biozal) wants to merge 2 commits into
mainfrom
portal-support

Conversation

@biozal

Copy link
Copy Markdown
Collaborator

What

Adds a dittosh server command group: a full HTTPS client for the Ditto Server (Big Peer) HTTP RPC API — the same API the portal's DQL editor uses. Endpoint inventory from the public docs (incl. the OpenAPI spec) and the portal's own client in cloud-services (portal/core/src/api/rpcClient.ts), which covers undocumented endpoints (RBAC, webhook secrets).

Commands

Command Endpoint
server execute (alias exec) POST /api/v5/store/execute (--api-version v4 = strict mode)
server remote-execute POST /api/v5/sync/remote_execute (peers, SYNC CONTEXT)
server attachment upload / get /api/v4/attachments (multipart up, raw bytes down)
server roles list/create/delete, server users list/set-roles/delete /api/v4/auth/* (portal RBAC)
server webhook-secrets list/create/rotate/delete /api/v4/auth/webhook/secret
server doctor validates config → connection → auth with a live probe

The legacy pre-DQL store API (find/findbyid/count/write) is deliberately omittedexecute covers it with full DQL (and its :param placeholders are rejected by the current deployment despite the OpenAPI text).

Configuration

--url/--api-key flags > shell env (DITTOSH_SERVER_URL/DITTOSH_SERVER_API_KEY, aliases DITTO_CLOUD_URL/DITTO_API_KEY) > .env in the current directory. The API key is never printed and is redacted from error messages. Exit codes follow the house contract (2 usage · 3 config/auth/connection · 1 query/API).

Every command's --help carries request body shapes, wire formats, and examples — most of these APIs aren't publicly documented, so the help text is the documentation.

Testing

  • 802 tests green. Unit: injectable FetchLike mock (no network), hermetic env scrubbing + tmpdir chdir (repo .env may hold real portal creds). e2e: real subprocess against a local node:http mock Ditto Server, execa extendEnv: false.
  • Coverage 90.8 / 86.2 / 96.2 / 92.1 vs the 85% hard gate.
  • Verified live against the retail app in the portal: SELECT/params/aggregates/EXPLAIN, full INSERT→SELECT→UPDATE→DELETE round-trip on a scratch collection, doctor with good + bad keys.
  • Adversarial review: 4 rounds, 2 independent reviewers, CONVERGED. ~25 issues fixed with regression tests (batch exit-code flattening, URL credential echo, --args - TTY hang, mid-body timeout mapping, roles-list pagination truncation, …). Ledger in plans/SDKS-4855-implementation-plan.md M9.
  • docs/testing-server.md: copy-pasteable manual checklist for the portal.

Notes

  • .env.sample documents the two new variables.
  • Not covered (deliberate): JWT http_login exchange, CDC/Kafka streams, server-side REPL.

New command group covering the portal HTTP API surface (public docs +
the portal's own rpcClient wire shapes):
- server execute (alias exec): DQL over HTTP — positional/-e/-f/stdin
  batch, -p/--args, --txn-id, --api-version v4|v5, all output formats,
  -o export, pager, --max-rows, --time, --continue-on-error
- server remote-execute: DQL on connected peers (SYNC CONTEXT)
- server attachment upload/get (multipart; binary-safe download)
- server roles list/create/delete, users list/set-roles/delete (RBAC)
- server webhook-secrets list/create/rotate/delete
- server doctor: config -> connection -> auth probe with source labels

Config: --url/--api-key > shell env (DITTOSH_SERVER_URL/_API_KEY,
aliases DITTO_CLOUD_URL/DITTO_API_KEY) > cwd .env. Key never printed;
redacted from errors. Legacy pre-DQL store API (find/findbyid/count/
write) deliberately omitted — execute covers it.

802 tests green (unit: mock FetchLike, hermetic env scrubbing; e2e:
node:http mock server, execa extendEnv:false). Coverage 90.8/86.2/
96.2/92.1 vs 85 gate. docs/testing-server.md manual checklist. Verified
live against the portal (retail app). Adversarial review: 4 rounds,
2 reviewers, CONVERGED.
Agreed majors:
- Fail closed on non-contract 2xx bodies: execute/remote-execute require
  DQL hallmark keys (no more silent [] on proxy/SSO HTML 200s; no raw
  TypeError on items:'string'); roles/users require their envelope keys
  (portal parity); webhook list keeps portal's [] fallbacks + 404 -> [].
- Reject non-loopback http:// — the cloud's 308 sent the key cleartext
  on hop 1, then stripped it on redirect -> misleading 401.
- --timeout flag (default 120s) + PortalTimeoutError: honest 'may still
  be running' message, exit 1 (a timeout is not proof of failure; the
  portal leaves DQL unbounded — 30s stays only for admin endpoints).
- roles create --help example was a shell syntax error in zsh AND bash
  (unescaped single quotes) — now uses " " inside JSON.
- docs/testing-server.md webhook section rewritten: the provider must
  already exist (verified live: 400 Provider not found).

Agreed minors: .env UTF-8 BOM strip; per-key .env hints; batch checks
stdoutBroken() (| head); users-list 404 unsupported-endpoint hint;
--permissions JSON strings checked against the blanket whitelist;
remote-execute sets DITTOSH_JSON_OUT + accepts leading comments;
printWarnings never prints 'undefined'; -o help includes ADVISE;
usage-first --api-version ordering; stripEq comment corrected;
No-statements error is red; dead ?? removed.

Process: e2e coverage gaps closed per AGENTS.md (doctor, users,
webhook-secrets, roles create/delete, attachment upload, remote-execute
happy path). README gained the full dittosh server feature section;
exit-code table updated.

Gates by exit code: lint 0, tsc 0, 765+ tests 0, coverage 90.7/86.3/
96.3/92.1 (gate 85), build 0. Live-verified against the portal.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looked at the auth stuff

- [x] `server remote-execute` — POST `/api/v5/sync/remote_execute`; client-side SYNC CONTEXT check (exit 2); per-peer JSON envelope out.
- [x] ~~Legacy store API~~ — **pulled post-review**: find/findbyid/count/write are the legacy pre-DQL API; `server execute` covers all of it with full DQL. (Verified live that legacy `:param` placeholders are rejected by the current deployment despite the OpenAPI text — another reason not to ship them.)
- [x] `server attachment upload|get` — multipart POST / byte GET; get refuses binary on a TTY without `-o`, pipes raw bytes otherwise.
- [x] `server roles list|create|delete` + `server users list|set-roles|delete` — RBAC endpoints the portal uses (undocumented publicly); both GET /roles wire shapes (bucketed + cursor-paged) normalized. Destructive deletes confirm (`-y` / TTY prompt / exit 2 piped).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing to note here. There are two different levels of user management that are possible.

  1. Control plane user management (The user data that the overall big peer and better auth stores)
  2. AppId/DatabaseId user management (the one currently implemented)

Since a user can be signed in to a big peer and be part of multiple databaseid's or be added and then removed from a databaseId's we have a concept of the user at the big peer level and a user at the databaseId level.

The Control plane user management is handled through BetterAuth API's today (we may wrap them eventually but it was not worth it given our time constraints for the initial launch of Auth v2)

Better Auth has both the Admin plugin, the organizations plugin and the SSO plugins enabled and the API's related to those if you wanted to also have this able to handle those as well

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants