Ship your first Drive in an afternoon.
A working Merkava Drive is fewer than 200 lines of Node.js. Seven endpoints, one HMAC signer, one manifest. By the end of this tutorial you'll have a runnable "Echo" Drive on your laptop, a validated manifest, and a developer application in flight. The full spec is the reference; this is the do-it path.
- What you'll need
- What you'll build (the Echo Drive)
- Step 1 · Initialize the project
- Step 2 · Verify HMAC on inbound requests
- Step 3 · Implement the seven endpoints
- Step 4 · Write your manifest
- Step 5 · Run locally + expose with HTTPS
- Step 6 · Validate the manifest
- Step 7 · Apply
- Common pitfalls
- What's next
What you'll need
- Node.js 18 or newer. The example uses native
fetch,crypto, and ESM. Bun and Deno work; the snippets are stock Node. - An HTTPS-reachable URL. Locally, ngrok or cloudflared tunnel works. For production, anything that gives you a public HTTPS endpoint (Railway, Fly, Render, your own VPS).
- A shared secret. The Platform Contract uses one symmetric secret per Merkava deployment. Approved developers receive theirs on application approval; for local development, invent a long random string and hold both sides of the conversation.
- An idea. A Drive does one specialist job well. "Email-warmup specialist." "Lighthouse-audit Drive." "Brand-asset reviewer." Don't build a Drive that's already in the Garage.
What you'll build
The Echo Drive. Every install records a row; the metrics endpoint reports the count. Pointless on its own, but it touches every required surface — health, metrics, events, billing, install, uninstall, manifest. Once Echo runs end-to-end, swap the inner implementation for whatever your Drive actually does. The Contract scaffolding doesn't change.
Final structure:
echo-drive/
package.json
.env.example
src/
server.js // Express app + the 7 endpoints
hmac.js // signRequest + requireHmac middleware
manifest.js // the manifest JSON your Drive publishes
store.js // in-memory install store (replace w/ DB later)
1Initialize the project
mkdir echo-drive && cd echo-drive npm init -y npm install express echo "MERIDIAN_AGENT_SECRET=invent-a-long-random-string-here-32-chars-min" > .env echo ".env" >> .gitignore
One dependency. The Platform Contract is plain HTTPS — no SDK to install, no codegen, no client libraries. Express is for ergonomics; you can do this with the bare http module.
2Verify HMAC on inbound requests
Merkava Core signs every authenticated request with the shared MERIDIAN_AGENT_SECRET. Two headers — timestamp (Unix ms, not seconds) and a raw-hex SHA-256 HMAC. Signed payload is exactly ${timestamp}:${path}. Path includes the query string. Method and body are NOT in the signature.
Create src/hmac.js:
'use strict';
const crypto = require('node:crypto');
const SKEW_TOLERANCE_MS = 5 * 60 * 1000;
function signRequest(path, secret, timestamp = Date.now()) {
const payload = `${timestamp}:${path}`;
const sig = crypto.createHmac('sha256', secret).update(payload).digest('hex');
return { 'X-Meridian-Timestamp': String(timestamp), 'X-Meridian-Signature': sig };
}
function verifyRequest(path, headers, secret, now = Date.now()) {
const ts = Number(headers['x-meridian-timestamp']);
const sig = headers['x-meridian-signature'];
if (!ts || !sig) return { ok: false, error: 'missing-headers' };
if (Math.abs(now - ts) > SKEW_TOLERANCE_MS) return { ok: false, error: 'timestamp-skew' };
const expected = crypto.createHmac('sha256', secret).update(`${ts}:${path}`).digest('hex');
if (expected.length !== sig.length) return { ok: false, error: 'sig-length' };
if (!crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(sig, 'hex'))) {
return { ok: false, error: 'sig-mismatch' };
}
return { ok: true };
}
function requireHmac(secret) {
return (req, res, next) => {
const path = req.originalUrl;
const result = verifyRequest(path, req.headers, secret);
if (!result.ok) return res.status(401).json({ ok: false, error: result.error });
next();
};
}
module.exports = { signRequest, verifyRequest, requireHmac };
=== on the hex strings leaks signature bytes through timing differences. crypto.timingSafeEqual compares in constant time. The length check before it guards against the exception timingSafeEqual throws when buffers differ in length.3Implement the seven endpoints
Two are public (/health, /drive/manifest). Five are HMAC-gated. Every response is wrapped in {ok, data, meta: {version, generated_at}}. Failures return {ok: false, error} with an appropriate HTTP status.
Create src/store.js first — the world's smallest install database:
'use strict';
const installs = new Map(); // install_id → { tenant_id, venture_id, created_at }
let events = [];
module.exports = {
installs,
recordInstall(tenantId, ventureId) {
const id = 'ins_' + Math.random().toString(36).slice(2, 12);
const row = { id, tenant_id: tenantId, venture_id: ventureId, created_at: Date.now() };
installs.set(id, row);
events.push({ type: 'install.created', payload: { install_id: id }, ts: Date.now() });
return row;
},
revokeInstall(id) {
const row = installs.get(id);
if (!row) return null;
installs.delete(id);
events.push({ type: 'install.revoked', payload: { install_id: id }, ts: Date.now() });
return row;
},
listEvents(since = 0) {
return events.filter((e) => e.ts > since);
},
};
Then src/server.js:
'use strict';
require('dotenv').config?.(); // or load .env however you prefer
const express = require('express');
const { requireHmac } = require('./hmac');
const store = require('./store');
const manifest = require('./manifest');
const app = express();
app.use(express.json());
const SECRET = process.env.MERIDIAN_AGENT_SECRET;
if (!SECRET) { console.error('MERIDIAN_AGENT_SECRET not set'); process.exit(1); }
const VERSION = '1.0.0';
const wrap = (data) => ({ ok: true, data, meta: { version: VERSION, generated_at: new Date().toISOString() } });
// 1. Health — public liveness probe
app.get('/api/meridian/health', (req, res) => res.json(wrap({ status: 'ok', uptime_s: Math.floor(process.uptime()) })));
// 2. Manifest — public; what the Garage reads
app.get('/api/meridian/drive/manifest', (req, res) => res.json(wrap(manifest)));
// 3. Metrics — HMAC; aggregated counters
app.get('/api/meridian/metrics', requireHmac(SECRET), (req, res) => {
res.json(wrap({ installs_active: store.installs.size }));
});
// 4. Events — HMAC; append-only log, ?since= filterable
app.get('/api/meridian/events', requireHmac(SECRET), (req, res) => {
const since = Number(req.query.since) || 0;
res.json(wrap({ events: store.listEvents(since) }));
});
// 5. Billing — HMAC; per-tenant billing summary (Echo is free)
app.get('/api/meridian/billing', requireHmac(SECRET), (req, res) => {
res.json(wrap({ tenant_id: req.query.tenant_id || null, plan: 'free', amount_cents: 0, currency: 'usd' }));
});
// 6. Install — HMAC; provision a new install, return scoped_token
app.post('/api/meridian/install', requireHmac(SECRET), (req, res) => {
const { tenant_id, venture_id } = req.body || {};
if (!tenant_id) return res.status(400).json({ ok: false, error: 'missing tenant_id' });
const row = store.recordInstall(tenant_id, venture_id || null);
// scoped_token authorizes future tenant-scoped reads from this Drive
res.json(wrap({ install_id: row.id, scoped_token: 'st_' + row.id, capabilities: ['read'] }));
});
// 7. Uninstall — HMAC; revoke an install
app.delete('/api/meridian/install/:id', requireHmac(SECRET), (req, res) => {
const row = store.revokeInstall(req.params.id);
if (!row) return res.status(404).json({ ok: false, error: 'not-found' });
res.json(wrap({ install_id: row.id, revoked: true }));
});
const PORT = Number(process.env.PORT) || 8787;
app.listen(PORT, () => console.log(`Echo Drive listening on :${PORT}`));
That's the entire Drive. Every endpoint has the same shape — wrap your data, attach meta, return ok:false with a plain-English error message on failure.
4Write your manifest
The manifest is the Drive's self-description. The Garage reads it to render your card; Merkava Core reads it on install to know what permissions you need. Create src/manifest.js:
'use strict';
module.exports = {
schema_version: 1,
drive: {
id: 'echo', // stable slug; lower-case, hyphenated
name: 'Echo',
tagline: 'Counts installs. Demonstrates the Platform Contract.',
category: 'Utilities',
motion: 'build', // build | scale | intel
homepage: 'https://echo-drive.example.com',
support_url: 'https://echo-drive.example.com/support',
icon_url: 'https://echo-drive.example.com/icon.png',
pricing_tier: 'free', // free | starter | standard | pro | premium | flagship
},
endpoints: {
health: '/api/meridian/health',
metrics: '/api/meridian/metrics',
events: '/api/meridian/events',
billing: '/api/meridian/billing',
install: '/api/meridian/install',
uninstall: '/api/meridian/install/:id',
manifest: '/api/meridian/drive/manifest',
},
capabilities: ['install', 'uninstall', 'metrics', 'events', 'billing'],
events_emitted: ['install.created', 'install.revoked'],
events_consumed: [], // add Merkava events your Drive listens for
contract_version: '1.0.0',
};
id is permanent. Once your Drive is approved and live, the slug becomes part of every install record, every payout reference, every operator's settings page. Pick one you can live with.5Run locally + expose with HTTPS
node src/server.js # Echo Drive listening on :8787 # In a second terminal — expose to the public internet ngrok http 8787 # Forwarding https://abcd-1234.ngrok-free.app -> localhost:8787
Sanity-check the public endpoints from anywhere — they don't need HMAC:
curl https://abcd-1234.ngrok-free.app/api/meridian/health # {"ok":true,"data":{"status":"ok","uptime_s":42},"meta":{"version":"1.0.0",...}} curl https://abcd-1234.ngrok-free.app/api/meridian/drive/manifest # Returns your manifest JSON
For HMAC-gated endpoints, sign the request yourself with the same secret to verify your verifier:
node -e '
const { signRequest } = require("./src/hmac");
const path = "/api/meridian/metrics";
const url = "https://abcd-1234.ngrok-free.app" + path;
const headers = signRequest(path, process.env.MERIDIAN_AGENT_SECRET);
fetch(url, { headers }).then((r) => r.json()).then(console.log);
'
6Validate the manifest
Before submitting, run your live manifest URL through the Merkava manifest validator. It checks the schema, the seven endpoints, and your HMAC handshake — the same gate the application form runs.
curl 'https://app.withmerkava.com/api/public/manifest-validate?url=https://abcd-1234.ngrok-free.app/api/meridian/drive/manifest'
# {"ok":true,"checks":{"schema":"pass","endpoints":"pass","hmac":"pass"}, ...}
If any check fails, the response tells you what — fix it, re-run. The validator is unauthenticated and CORS-open, so you can wire it into your CI / pre-submit script.
7Apply
Submit your manifest URL through the developer application form. We approve developers individually — keeping the bar high keeps the Garage trustworthy for operators.
After approval you receive your scoped MERIDIAN_AGENT_SECRET, a developer dashboard at app.withmerkava.com/developer, and a slot in the admin listing review queue. After your listing is approved, your Drive shows up in the Garage and operators can install. Stripe Connect onboarding happens once — payouts auto-flow at 70% of every operator's invoice.
Common pitfalls
Math.floor(Date.now() / 1000) works locally because both sides are wrong; production fails because Merkava signs in ms.sha256= in the signatureX-Meridian-Signature: 5f3c....${timestamp}:${path} is signed. Path includes query string. Method, body, and other headers are not in the signature./health and /drive/manifest are public. Crawlers hit your manifest. If you require HMAC there, the Garage can't render your card.{ok, data, meta} on success or {ok:false, error} on failure. Returning bare data is treated as a malformed response.MERIDIAN_AGENT_SECRET never leaves the server. If you find yourself prefixing it with NEXT_PUBLIC_ or VITE_, stop and rethink.Quickstart — questions, plainly answered.
How long does the quickstart actually take?
An afternoon for a developer comfortable with Node.js. The seven endpoints are mostly boilerplate — manifest, install, uninstall, event-receive, health, manifest-fetch, webhook. The actual work is the HMAC signing/verifying scaffolding, which the cookbook at /resources/drive-hmac handles in five languages. Production-readiness (config UI, error handling, observability) is a separate effort beyond the quickstart.
What do I need before starting?
Three things: Node.js 18+, an HTTPS-reachable URL (use ngrok or a Railway/Render free tier for development), and a Merkava developer account. Apply at /resources/developers if you don't have one. The tutorial is Node-specific but the underlying concepts apply identically to Python, Go, Ruby, or PHP.
Can I run the quickstart entirely locally?
Yes for development. You'll need an HTTPS tunnel (ngrok is the standard) so Merkava Core can reach your local Drive at install time. The signing handshake works fine over ngrok; once your Drive is reachable, Core treats it the same as any cloud-hosted Drive. Switch to a real host when you're ready to publish.
Does the quickstart Drive get listed in the public Garage?
No — quickstart Drives stay private to your developer account by default. To list publicly: complete the quickstart, refine against the production checklist (scope minimization, error handling, observability), then submit through the developer portal. Listing review checks manifest validity, scope minimization, security posture, and claimed-vs-actual capabilities.
What's the minimum a quickstart Drive needs to do?
Respond to the seven required endpoints, validate inbound HMAC signatures, sign outbound events, and publish a valid manifest. The tutorial walks through an "Echo" Drive that does all seven correctly. From there you replace the Echo handler with your actual business logic — the boilerplate stays the same.
What if I get stuck during the quickstart?
Run the published HMAC test vectors at /resources/drive-hmac-test-vectors against your sign function — that catches 80% of stuck-at-handshake bugs. Email [email protected] with the manifest, your Drive URL, and the error message; the dev-relations response is same-day during the early-customer phase.
Can I follow the quickstart in a language other than Node.js?
The narrative steps work in any language. Replace the Node.js code blocks with the equivalent from the multi-language cookbook — Python, Go, Ruby, and PHP are byte-for-byte equivalent. The signing and event handling logic is identical across stacks; only the syntax differs.
What's next
- Drive Author spec → — the full reference. Read it once you've shipped Echo and want to deepen.
- Platform Contract spec → — signing, payload shape, error semantics, version negotiation.
- Drive manifest reference → — every field, every option, every constraint.
- Developer portal overview → — pricing tiers, featured tiers, the developer agreement.
- Developer Agreement v1.0.0 → — the terms you accept on application. Read before you submit.
- Browse existing Drives → — see what's already in the Garage. Don't duplicate; differentiate.
If Echo runs and your manifest validates, you're ready. Submit the form; we'll get back within 3 business days.