Gebaut für die Dinge,
die durch den Einkauf müssen.

Ein Framework, das ein Enterprise-Team einführen kann, muss eine längere Fragenliste bestehen als ein Startup: Wie funktioniert Multi-Tenancy, wohin gehen Audit-Events, können wir selbst hosten, läuft es auf unserem managed Postgres, wie sieht die Cluster-Story aus, wie fließen Traces. Voltro beantwortet jede davon mit einem Primitiv, nicht mit einem TODO. Gebaut auf Effect-TS — derselben Runtime, die Microsoft, Block und Discord heute in Produktion betreiben.

values-prod.yaml
Helm
# values-prod.yaml — Voltro's Helm chart, real cluster.
voltro:
  image:
    repository: registry.acme.com/voltro/acme
    tag:        v1.0.0
  replicas: 6

postgres:
  embedded: false                # Use the managed RDS / Cloud SQL
  url: ${POSTGRES_URL}            # Pulled from a sealed Secret

readReplicas:
  urls: ${POSTGRES_REPLICA_URLS} # Comma-separated
  regions: us-east-1,us-west-2

cache:
  backend: redis                 # ElastiCache cluster
  url: ${REDIS_URL}

observability:
  otelEndpoint: http://otel-collector.observability:4318
  serviceName: acme-app

session:
  secret: ${VOLTRO_SESSION_SECRET}  # From sealed Secret

audit:
  sink: console                  # Pipe stdout to Datadog / Splunk

# kubectl apply -f values-prod.yaml --namespace=acme-prod

Sechs Anforderungen, sechs eingebaute Antworten.

Effect-TS-Internals in Produktionsqualität

Voltro ist auf Effect-TS gebaut — der Runtime, die Microsoft Azure, Block, Discord und andere heute in Produktion einsetzen. Strukturierte Nebenläufigkeit, typisierte Fehler, durable Execution, OpenTelemetry — alles Features der zugrunde liegenden Plattform, auf die wir bauen, nicht Features, die wir nachbauen.

Postgres-first, Multi-Dialekt-tolerant

Postgres ist der empfohlene primäre Store; MariaDB / MySQL / MSSQL / SQLite sind erstklassig für Beschaffungsvorgaben im Enterprise. Read-Replicas, regionsbewusstes Routing, RYW-Konsistenzpolicy (read-your-writes) — alles per Env-Var zuschaltbar.

Row-Level-Isolation über den tenant-Mixin

Der tenant()-Schema-Mixin merged tenantId per AND in jeden Read auf der Runtime-Ebene. Cross-Tenant-Zugriff ist konstruktionsbedingt unmöglich — nicht durch eine handgeschriebene WHERE-Klausel, die jemand vergisst.

Vollständiges Audit-Log

@voltro/plugin-audit protokolliert jeden Mutation-Aufruf (Tag, Subject, Input, Ergebnis, Dauer) in einen konfigurierbaren Sink — Konsole im Dev, eigener Effect für "nach Splunk/Datadog/SOC2-Nachweis leiten". Der Schema-Level-audit()-Mixin ergänzt createdAt/updatedAt/createdBy/updatedBy → actors auf jeder Zeile.

OpenTelemetry, Ende-zu-Ende

Jedes Primitiv emittiert OTel-Spans: client.mutation, Server-Mutation, store.transactional, Action, Subscription-Delivery, Webhook. Eine traceId reicht von React useMutation bis zu deinem nachgelagerten API-Aufruf. Der Helm-Chart liefert einen OTEL_EXPORTER_OTLP_ENDPOINT-Verdrahtungspfad.

Heute self-hosten; Managed Cloud kommt bald

Der Helm-Chart, der Docker-Compose-Stack, die systemd-Baseline — alles in deinem Repo, alles unter deinem Ops-Team. Voltro Cloud wird ein managed Deploy derselben Primitive für Teams, die die Infra nicht selbst betreiben wollen — es kommt bald. So oder so muss Engineering nicht zwischen zwei Stacks wählen.

Interop mit der Infrastruktur, die du schon betreibst

Ein Voltro-Backend konsumiert und produziert gegen dein BESTEHENDES Kafka (@voltro/plugin-queue: Schema-dekodierte Consumer, seriell pro Partition, Retry + Dead-Letter; transaktionales Produzieren über die Outbox), spiegelt Tabellen-Changes dorthin (cdc-out kafkaSink) und serviert Opt-in-Prozeduren an generierte gRPC-Clients — das .proto aus deinem effect/Schema mit eingecheckter Feldnummern-Stabilität, Guards und typisierte Fehler identisch zum Socket. Nachbarsysteme behalten ihre Verträge — die Adoption beginnt nicht mit einer Migration von allem drumherum.

API-Versionierung mit deklariertem Sunset-Flow

Eine REST-Route kann in mehreren Versionen nebeneinander existieren — defineRestRoute nimmt ein First-Class-version-Feld (/v1, /v2 als separate Descriptors, kein Transformations-DSL), die Deprecation-Header und der 410-Tombstone nennen Version und Nachfolger, und das OpenAPI-Dokument listet jede Version mit Gruppierungs-Tag. Konsumenten migrieren nach deinem publizierten Zeitplan, nicht an deinem Deploy-Tag.

Das Framework kämpft nicht gegen dein Plattform-Team.

Enterprise-Plattform-Teams haben bereits standardisiert — auf managed Postgres, auf einen k8s-Cluster, auf einen zentralen Observability-Stack, auf ein Sealed-Secret-Tool. Ein Framework, das seine eigene Datenbank, seinen eigenen Scheduler, seinen eigenen Observability-Anbieter, seinen eigenen Auth-Flow verlangt, IST das Integrationsprojekt. Voltro ist das Framework, das nutzt, was dein Plattform-Team ohnehin schon betreibt.

Die Compliance-freundliche Story:

  • Auth. @voltro/plugin-auth liefert HttpOnly-Session-Cookies, signiert per HMAC-SHA256 mit fest verdrahtetem Algorithmus (keine JWT-alg-Confusion). API-Keys über apiKeyStrategy mit SHA-256-Hash-Lookup. JWT-Bearer über JWKS via jwtBearerStrategy.
  • RLS. Der tenant()-Schema-Mixin erzwingt Row-Level-Isolation auf der Runtime UND im DDL. SOC2-/ISO-27001-Auditoren bekommen eine klare "Zeilen sind pro Tenant partitioniert"-Story ohne eigene Postgres-RLS-Policy.
  • Audit-Log. Jede Mutation landet im Audit-Sink. Leite ihn an dein SIEM. Der Schema-Mixin stempelt WER + WANN auf jede Zeile. "Wer hat diesen Kundendatensatz vor drei Monaten geändert" ist eine Ein-Query-Antwort.
  • Data Residency. Multi-Region-Read-Replicas mit DB_REPLICA_REGIONS-Routing. Pinne Reads auf regionsinterne Replicas. Writes immer auf die Primary.
  • Observability. Die Trace-ID propagiert frontend → API → nachgelagert. Jede Log-Zeile trägt sie. Jeder Span trägt sie. Untersuche Fehlschläge und betrachte die GESAMTE Kausalkette mit voltro logs --trace.

Was ein Request vor deinem Handler durchläuft.

  1. 1

    Die Credential wird zum Subjekt

    Session, API-Key oder Bearer-Token — oder keines, dann ist der Aufrufer anonym statt vertrauenswürdig. Der Widerruf wird hier geprüft, nicht an der ersten Query.

  2. 2

    Autorisierung läuft an der Deklaration

    Guards sitzen an der Prozedur, jeder Einstiegspunkt geht also durch sie. Eine Regel im Handler ist eine, die ein zweiter Einstiegspunkt umgeht.

  3. 3

    Das Mandantenprädikat wird eingemerged

    Reads werden von der Runtime auf den Mandanten des Aufrufers begrenzt, nicht von einem Filter, an den jede Query denkt. Es überhaupt aufzuweiten ist eine ausdrückliche, benannte Operation.

  4. 4

    Die Arbeit wird festgehalten, während sie passiert

    Die Mutation landet mit Subjekt und Ergebnis in der Audit-Senke, die Zeile trägt, wer sie wann geändert hat, und ein Trace-Identifier verbindet die ganze Kette.

Die Fragen, die ein Security-Review tatsächlich stellt.

Wie wird Mandantentrennung erzwungen, und was könnte sie brechen?

Mandantenfähigkeit ist ein Schema-Mixin, und die Runtime merged das Mandantenprädikat in jeden Read, statt darauf zu vertrauen, dass jede Query eines hinzufügt. Diese Unterscheidung ist das ganze Argument: ein handgeschriebener Filter ist korrekt, bis jemand die Query schreibt, die ihn vergisst — und die vergessene ist nie die, die jemand reviewt.

Die Durchsetzung ist bewusst schwer versehentlich abzuschalten. Wo die Grenze überhaupt gelockert werden kann, ist das eine ausdrückliche, benannte Operation statt eines stillen Defaults, und die Pfade des Frameworks gehen durch dasselbe Gate wie Anwendungscode. Eine Regel, von der sich das Framework selbst ausnimmt, wird auch anderswo ausgenommen.

Dieses Design stammt aus einem echten Defekt: Zustand, der über Zugriff entschied, lag modul-lokal und spaltete sich pro Instanz, sodass mehrere Lesepfade einen leeren Filter sahen und den ganzen Mandanten zurückgaben. Der Fix machte den Zustand prozessglobal und einen ungesetzten Wert eindeutig statt großzügig — die allgemeine Lehre: fail-open-Defaults sind der Fehlermodus, gegen den man konstruieren sollte.

Welche Nachweise erzeugt das System für ein Audit?

Mutation-Aufrufe landen in einer Audit-Senke — Tag, handelndes Subjekt, Eingabe, Ergebnis und Dauer — und die Senke ist konfigurierbar, die Einträge können also in das SIEM oder den Nachweisspeicher fließen, den die Organisation ohnehin betreibt. Audit ist kein Logformat, das man nachträglich rekonstruiert.

Zeilenbezogene Herkunft ist davon getrennt und ergänzend. Ein Audit-Mixin stempelt Erstellungs- und Änderungszeitpunkt zusammen mit dem handelnden Subjekt auf jede Zeile — Fragen wie wer hat diesen Datensatz wann geändert werden damit zu einer einzigen Query statt zu einer Logsuche über Aufbewahrungsfenster hinweg.

Traces verbinden beides. Ein Trace-Identifier fährt vom Browser-Aufruf durch die Server-Mutation, die Datenbanktransaktion, ausgelöste Hintergrundarbeit und den ausgehenden Webhook — ein Fehler lässt sich also als eine Kausalkette untersuchen statt als vier korrelierte Systeme.

Läuft das auf unserer Plattform, oder bringt es eine eigene mit?

Es konsumiert, was dein Plattformteam ohnehin betreibt. Postgres ist der empfohlene primäre Store, MySQL, MariaDB, SQL Server und SQLite sind erstklassig unterstützt, weil Beschaffungsvorgaben real und selten verhandelbar sind. Read-Replicas und regionsbewusstes Read-Routing sind Konfiguration, kein Fork.

Es gibt keinen verpflichtenden Broker, Workflow-Dienst oder Cache-Layer. Durable Workflows journalisieren in die Datenbank, geplante Jobs koordinieren sich darüber, und das Echtzeit-Fan-out nutzt den Change-Feed der Datenbank auf den Engines, die einen bieten. Jedes davon lässt sich später auf dedizierte Infrastruktur zeigen, keines ist Voraussetzung für ein erstes Deployment.

Deploy-Baselines gibt es für Compose, Helm und ein schlichtes systemd-Ziel, und Observability exportiert über OpenTelemetry an den Collector, der schon existiert. Die Haltung des Frameworks: die Standardisierung eines Plattformteams ist ein Aktivposten, den man nutzt, kein Hindernis, das man umfährt.

Wie sehen Upgrades und langfristige Wartung aus?

Schema-Änderungen sind deklarativ. Die Schema-Datei ist der Sollzustand, der Planer diffed sie gegen die Live-Datenbank, druckt die beabsichtigten Operationen und klassifiziert die destruktiven — ein Migrations-Review ist damit ein Review von Absicht statt Archäologie an handgeschriebenen Änderungsskripten.

Breaking Changes des Frameworks liefern Codemods statt Upgrade-Prosa. `voltro update` hebt die Pakete, gleicht Peer-Versionen an und schreibt den betroffenen Quellcode um — das hält einen großen Bestand davon ab, auf einer alten Version einzufrieren, weil die Migrationskosten den Willen überstiegen.

Eine Einschränkung, die klar gesagt gehört, weil sie in Produktion zugebissen hat: Konfiguration, die das deklarierte Schema verändert, muss zwischen dem Prozess, der migriert, und dem, der Traffic bedient, übereinstimmen. Ein Migrations-Job mit anderen Flags als der Pod erzeugt ein grünes Apply und einen CrashLoop — deshalb leiten die Prüfungen die Erwartung ab, statt jemanden zwei Umgebungen von Hand im Gleichschritt halten zu lassen.

Anforderung zu Mechanismus.

Übliche Review-Anforderungen und der Mechanismus dahinter
AnforderungMechanismus
MandantentrennungSchema-Mixin mit runtime-erzwungenem Prädikat-Merge bei jedem Read statt Filtern pro Query.
AuthentifizierungFirst-Party-Sessions, MFA, Passkeys und API-Keys; SSO und SCIM-Provisionierung als Plugins.
AutorisierungDeklarative Guards an jedem Einstiegspunkt plus Rollen- und Scope-Prüfungen, serverseitig ausgewertet.
AuditierbarkeitAudit-Senke auf Mutationsebene plus Akteur- und Zeitstempel auf Zeilenebene.
DatenresidenzRegionsbewusstes Read-Routing über Replicas; Writes auf den Primary; durchgehend self-hosted.
ObservabilityOpenTelemetry-Spans über Client, Server, Datenbank und Hintergrundarbeit unter einem Trace.

Häufige Fragen

Müssen wir eure Cloud nutzen?

Nein. Self-Hosting ist der Default und der voll unterstützte Weg — deine Infrastruktur, deine Datenbank, deine Netzgrenze. Zur Laufzeit ruft nichts bei uns an, und die Managed Cloud ist ein separates, optionales Produkt.

Welche Datenbanken werden wirklich unterstützt?

Postgres ist empfohlen; MySQL, MariaDB, SQL Server und SQLite sind erstklassig und werden über Dialekt-Paritätstests geprüft statt als kompatibel angenommen. Einzelne Fähigkeiten, vor allem datenbankgetriebenes Echtzeit-Fan-out, unterscheiden sich je Engine — die Docs sagen wo.

Wie passt SSO hinein?

Single Sign-on und Verzeichnis-Provisionierung sind Plugins neben der eingebauten Session-Schicht. Eine Anwendung kann mit lokalen Konten starten und Enterprise-Identität ergänzen, wenn ein Kunde sie verlangt, ohne ihr Autorisierungsmodell umzubauen.

Was kostet uns die Effect-Abhängigkeit?

Sie ist der Grund, warum typisierte Fehler, strukturierte Nebenläufigkeit und durable Execution Eigenschaften der Runtime sind statt Dinge, die wir nachbauen. Alltäglicher Anwendungscode liest sich wie gewöhnlicher async-Code; die Tiefe ist da, wenn ein Problem sie braucht.

Wie evaluieren wir es ohne Beschaffungszyklus?

Scaffolde ein Projekt gegen eine Wegwerf-Datenbank und lass es lokal laufen — zum Evaluieren braucht es weder Konto noch Lizenzserver noch ausgehende Verbindung. Lizenziert wird pro Entwicklersitz, und zwar wenn du damit baust, nicht wenn du es liest.

Die Features, nach denen dein Security-Review fragen wird.

Öffne das Framework. Schau es dir selbst an.

Jede Primitive auf dieser Seite ist heute im Framework. Klone den Starter, lass `voltro dev` laufen, in zwei Minuten ist es auf dem Bildschirm.