Build your own client or renderer
Everything a client needs is documented and standard-crypto based, so you can build one in any stack. There are two things to build: a submit client (no UI) or a renderer (UI + submit).
A submit client
Goal: take a .dmf + a values map → a sealed submission.
-
Read + verify the
.dmf(format):- Unzip; read
manifest.json,signature.bin,form.json. - Ed25519
verify_detached(signature.bin, manifest.json bytes, signer.publicKey). - SHA-256 of
form.jsonmust equal itsfiles[]entry. - Read
recipient.publicKey,recipient.userId, and fromform.jsontheid+schemaVersion+fields.
- Unzip; read
-
Validate + coerce values against the field kinds: required present, unknown keys rejected, choice membership, per-kind value shapes.
-
Build the SubmissionPayload, JSON-encode (UTF-8).
-
Sealed-box encrypt the bytes against
recipient.publicKey(crypto_box_seal), base64 the ciphertext. -
POST the SubmissionEnvelope as JSON to the public submissions endpoint:
POST https://datamaker-api.fobo-tools.com/submissionsContent-Type: application/jsonIt’s anonymous (no auth) for public forms. 200 →
{ submissionId, editToken }; 400 → bad/missing envelope fields; 413 → ciphertext too large. See Submission & encryption for the exact envelope + payload shapes.
If the form has image / attachment fields, upload the bytes before
step 3 and put the resulting ref into values — see
Files & attachments. Bytes never go in the
sealed payload.
You need a libsodium binding (sealed box + Ed25519 verify), a ZIP reader, SHA-256, and JSON. That’s it.
crypto you need: crypto_box_seal(plaintext, recipientPubKey) → submission encryption Ed25519 verify_detached(sig, msg, signerPub) → .dmf signature check SHA-256 → .dmf hash checkThe official SDKs (JS, Python, .NET, PHP) are reference implementations — read their source if you get stuck.
A renderer
A renderer additionally turns form.json into UI and collects values.
- Read the form (as above) — you also want the
fields+stepslayout (form model) and, from a.dmf v3, the styling entries (elementCss.json,palette.css,fonts.css). - Render the layout: walk
steps → sections → rows → columns; render eachfieldcolumn with the editor for its kind; render decorative columns (richtext/image/divider/heading/button/group). - Evaluate expressions for
visibleWhen/calculatedExpression/ validation — either run the bundle’s compiled JS or implement the DSL + Fn library. When you can’t evaluate: fail open forvisibleWhen(show the field); emitcalculatedExpressionfields empty + flag them uncomputed — never editable. - Validate on submit, then hand the values to the submit-client steps above.
Trust & safety checklist
- Always verify the signature and hashes before trusting anything in the
bundle — especially
recipient.publicKey. - Treat the signer’s claimed identity as unverified unless a valid FOBO attestation is present.
- Never log or persist plaintext values beyond what your client needs; the whole point is the server can’t read them.
- Match the wire contract exactly (camelCase keys, base64
ORIGINAL, UUID without dashes,formVersion = schemaVersion).