Native Shell
Native Shell:AI 生成的 UI,在你的应用中原生呈现,覆盖每个平台。iOS、Android、macOS、Windows 与 Linux 上的应用、GPUI 应用与网页在运行时加载已签名、沙箱隔离的 TSX 模块并绘制 th
AI 翻译
A Cameroon-first, extensible A2P SMS gateway for OTPs and notifications: schema-driven Rust services with OIDC security, multi-provider HTTP/SMPP delivery, DLRs, signed webhooks, and fault-tolerant PostgreSQL workers.
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗An A2P SMS gateway for Cameroon — OTP and notification delivery over MTN and Orange, with a provider abstraction that covers both HTTP APIs and SMPP.
Status (snapshot, 2026-08-13 — docs/roadmap.md is the maintained version, and GitHub Issues is the real tracker): foundations and delivery are done. Schema, migrations, encoding, and MSISDN parsing; a signed transport (every request a COSE_Sign1 message under a registered key, no tokens) and console pairing in place of a login; a real worker — leader election, optimistic-CAS claim loops, the message and job state machines, DLR ingestion, a job queue; and signed, at-least-once webhooks with retries and circuit breaking. Retention, audit anchoring, backup with a real restore drill, and alerting are done. The admin console is partly built (messages, dashboard, jobs, workers, providers, routes); MTN, the routing rules engine, and failover have landed.
One thing gates real traffic, and it is not code: #36 — no message from this system has ever reached a real handset. That needs a human with a real Orange account and a real phone. Everything automated around it proves how this system behaves under faults; none of it proves Orange behaves the way this code assumes.
You can run all of it locally, with no carrier account, in one command — see local development.
Which door you want depends on what you're doing:
| You want to… | Start here |
|---|---|
| Send SMS through vsms from your own application | docs/integrating.md — credentials and signing, sending, message states, webhooks |
| Run the whole thing locally, no carrier account | docs/runbooks/local-development.adoc — just demo and what it gives you |
| Work on vsms itself | docs/runbooks/getting-started.adoc, then CONTRIBUTING.md |
| Run the entire CI gate before opening a PR | docs/runbooks/testing.adoc — just ci, one command, only docker/docker compose/just needed on the host |
| Deploy or operate it | docs/runbooks/deployment.adoc and docs/runbooks/ |
The short version, for a developer who just wants it running:
git clone https://github.com/vaam-apps/vsms.git
cd vsms
just demo
That brings up a scratch Postgres, the gateway, the worker, a fake Orange, and the admin console, wired together with a provisioned client — and a message reaches delivered without a single real SMS being sent. docker compose -f compose.dev.yaml --profile console logs demo-app shows the whole round trip proved for you, end to end, including a real signed webhook that verifies (examples/node/demo-app).
| Path | What it is |
|---|---|
docs/integrating.md | For a developer sending SMS through vsms. Credentials and request signing, request and response shapes, message states, status codes, webhooks. |
docs/architecture.md | The full design. Data model, provider abstraction, worker topology, security, compliance, and every framework constraint that shaped a decision. |
docs/roadmap.md | Sequencing: what order the work happens in, what genuinely blocks what, and what has to be true before real traffic. |
OPEN_QUESTIONS.md | What this system does not know the answer to — human decisions, unverified claims, accepted limitations. |
docs/runbooks/ | Step-by-step operational procedures: local development, deployment, backup and restore, alerting, and the real-handset acceptance gate. |
AGENTS.md | Current project status in detail: what's built, what's still open, and every non-obvious thing found by actually running the toolchain. |
CONTRIBUTING.md | The three rules (R1/R2/R3) the codebase is written against, and the workflow for changing the schema. |
schemas/vsms.cstack | 28 models, 21 enums, 24 procedures. Parses, emits Postgres DDL, and expands through include_server_schema!. |
backends/migrations/postgres/0001_init/ | Generated by cratestack migrate diff. Do not hand-edit — regenerated wholesale on every schema change; see AGENTS.md. |
backends/migrations/postgres/0002_bootstrap/ | Everything the emitter doesn't produce: id and timestamp defaults, the updated_at trigger, the state machines, partial indexes, foreign keys. Generated from §2.10 of the design doc. |
backends/crates/sms-encoding/ | GSM 03.38 vs UCS-2 analysis, segment counting, normalisation, transliteration. |
backends/crates/sms-msisdn/ | E.164 +237 parsing, line-type classification, operator-prefix lookup. |
backends/crates/sms-api/ | Where include_server_schema! expands: generated models, policies, the REST router, the procedure registry, DLR ingestion, and the webhook subscribers. |
backends/crates/ is libraries, backends/apps/ is binaries, and nothing in backends/crates/ depends on anything in backends/apps/.
=0.17.2 — schema-first, .cstack generates the model layer, policies, audit, events and REST surface. Pinned exactly, and the installed CLI must match the pin; see AGENTS.md.docs/architecture.md §4).@version (SKIP LOCKED isn't expressible through the framework), leader election via advisory locks, and the state machines enforced by triggers. No broker, no Redis, no consensus library.Three findings shaped the schema more than any product requirement, and each is a property of the toolchain rather than of the problem:
String[] parses fine and the migration emitter writes a cheerful TEXT[], but include_server_schema! dies with unsupported SQLx value type for this slice. Every multi-value column here is a sentinel-delimited string instead.@default(...) excludes a field from the create input — literals included. A @default on something a caller must set makes it unwritable. It's kept only where being unsettable is the point, such as Message.state, which is why no client can create a message that is already delivered.@server_only excludes a field from create and update, so it can never be populated at all. It is for columns the database owns, not for secrets you write.None of these are documented upstream. Every framework constraint found this way — not just these three — is recorded in §2.0 with the exact error each one produces, and in AGENTS.md with the story of how it was found.
MIT — see LICENSE.
backends/crates/sms-auth/ | The embedded OpenID Provider's stores, signing keys and machine-client provisioning (VSA-3a; restored from before #446, behind auth.methods and op:), the console's client row and the Argon2id password login (VSA-4a, behind human_login:). The single-sign-on broker (VSA-4c, behind human_login.sso: discovery, id_token checks, group gate, MFA rule, just-in-time accounts). |
backends/crates/sms-worker/ | The worker as a library: role selection, advisory-lock leader election, the CAS claim loop, and each role's real body (dispatch, scheduler, jobs, drain, hooks). |
backends/crates/sms-webhook/ | Outbound webhooks in two modes: sealed COSE_Sign1 requests under the gateway's own envelope key (VS-8), and the original HMAC-SHA256 signature (hmac_v1); both cross-checked in CI. |
backends/crates/sms-config/, config/ | The YAML deployment configuration both binaries read (VSC-1): config/application.yml plus a profile overlay, ${VAR} placeholders for secrets, and the refusal of every flag and variable it replaced. Reference: docs/architecture.md, section 9.4. |
backends/crates/sms-provider/ | The SmsProvider trait every adapter implements — capabilities, submit, DLR parsing, health — framework-free. |
backends/crates/sms-provider-orange-cm/, backends/crates/sms-provider-mtn/ | The Orange Cameroon and MTN adapters. |
backends/crates/sms-fake-orange/ | A fault-injecting fake of Orange's API — a participant, not a response stub. Development and testing only. |
backends/crates/sms-fake-oidc/ | A fake upstream OpenID Provider (a Keycloak stand-in) for the single-sign-on tests: it signs whatever id_token a test scripts, malformed ones included, with throwaway keys it generates at run time. Dev/test-only. |
backends/apps/sms-gateway/ | The API server binary. serve binds HTTP behind the signed transport; register-client, pair-console-key, server-keys, seed-dispatch, provision-user, bootstrap, create-app, record-route-validation and mtn-subscribe-dlr are the operator actions; routes prints the route table and healthcheck is the exec-form liveness/readiness probe — neither needs a database. |
backends/apps/sms-worker/ | The role-selectable worker binary (--roles dispatch,scheduler,jobs,...) — see its own module doc for why the package is sms-worker-bin but the binary is sms-worker. |
frontends/apps/admin/, frontends/packages/ | The Next.js admin console and its shared TypeScript packages. |
sdks/rust/, examples/ | The Rust SDK, and runnable Rust and Node integration examples including a reference webhook receiver. |
deploy/ | Compose stack, Helm chart, Caddy edge, migration job, backup and restore. |
ci/ | The state-machine SQL test (ci/test-state-machine.sql), the generated-client route gate, the mermaid-diagram parse checker, and the standalone e2e-integration tool. R1/R2/R6, the bootstrap-SQL generator, and the migration-drift checks are cargo xtask <check> now — see .xtask/. |