[{"data":1,"prerenderedAt":445},["ShallowReactive",2],{"navigation_docs":3,"-getting-started-architecture":136,"-getting-started-architecture-surround":440},[4,26,47,68,89,110,123],{"title":5,"path":6,"stem":7,"children":8,"page":25},"Getting Started","\u002Fgetting-started","1.getting-started",[9,13,17,21],{"title":10,"path":11,"stem":12},"What you are running","\u002Fgetting-started\u002Fwhat-you-are-running","1.getting-started\u002F1.what-you-are-running",{"title":14,"path":15,"stem":16},"Architecture","\u002Fgetting-started\u002Farchitecture","1.getting-started\u002F2.architecture",{"title":18,"path":19,"stem":20},"Prerequisites","\u002Fgetting-started\u002Fprerequisites","1.getting-started\u002F3.prerequisites",{"title":22,"path":23,"stem":24},"Quickstart","\u002Fgetting-started\u002Fquickstart","1.getting-started\u002F4.quickstart",false,{"title":27,"path":28,"stem":29,"children":30,"page":25},"Install","\u002Finstall","2.install",[31,35,39,43],{"title":32,"path":33,"stem":34},"Install the chart","\u002Finstall\u002Finstall-the-chart","2.install\u002F1.install-the-chart",{"title":36,"path":37,"stem":38},"Activation","\u002Finstall\u002Factivation","2.install\u002F2.activation",{"title":40,"path":41,"stem":42},"DNS and TLS","\u002Finstall\u002Fdns-and-tls","2.install\u002F3.dns-and-tls",{"title":44,"path":45,"stem":46},"Verify your install","\u002Finstall\u002Fverify-your-install","2.install\u002F4.verify-your-install",{"title":48,"path":49,"stem":50,"children":51,"page":25},"Configure","\u002Fconfigure","3.configure",[52,56,60,64],{"title":53,"path":54,"stem":55},"Data services","\u002Fconfigure\u002Fdata-services","3.configure\u002F1.data-services",{"title":57,"path":58,"stem":59},"Secrets","\u002Fconfigure\u002Fsecrets","3.configure\u002F2.secrets",{"title":61,"path":62,"stem":63},"Registry and air-gapped installs","\u002Fconfigure\u002Fregistry-and-airgap","3.configure\u002F3.registry-and-airgap",{"title":65,"path":66,"stem":67},"Integrations","\u002Fconfigure\u002Fintegrations","3.configure\u002F4.integrations",{"title":69,"path":70,"stem":71,"children":72,"page":25},"Licensing","\u002Flicensing","4.licensing",[73,77,81,85],{"title":74,"path":75,"stem":76},"How licensing works","\u002Flicensing\u002Fhow-licensing-works","4.licensing\u002F1.how-licensing-works",{"title":78,"path":79,"stem":80},"Your key never leaves","\u002Flicensing\u002Fyour-key-never-leaves","4.licensing\u002F2.your-key-never-leaves",{"title":82,"path":83,"stem":84},"What phones home","\u002Flicensing\u002Fwhat-phones-home","4.licensing\u002F3.what-phones-home",{"title":86,"path":87,"stem":88},"Revoke and recover","\u002Flicensing\u002Frevoke-and-recover","4.licensing\u002F4.revoke-and-recover",{"title":90,"path":91,"stem":92,"children":93,"page":25},"Operate","\u002Foperate","5.operate",[94,98,102,106],{"title":95,"path":96,"stem":97},"Upgrade","\u002Foperate\u002Fupgrade","5.operate\u002F1.upgrade",{"title":99,"path":100,"stem":101},"Backup and restore","\u002Foperate\u002Fbackup-and-restore","5.operate\u002F2.backup-and-restore",{"title":103,"path":104,"stem":105},"Observability","\u002Foperate\u002Fobservability","5.operate\u002F3.observability",{"title":107,"path":108,"stem":109},"High availability","\u002Foperate\u002Fhigh-availability","5.operate\u002F4.high-availability",{"title":111,"path":112,"stem":113,"children":114,"page":25},"Troubleshooting","\u002Ftroubleshooting","6.troubleshooting",[115,119],{"title":116,"path":117,"stem":118},"Install failures","\u002Ftroubleshooting\u002Finstall-failures","6.troubleshooting\u002F1.install-failures",{"title":120,"path":121,"stem":122},"Licensing failures","\u002Ftroubleshooting\u002Flicensing-failures","6.troubleshooting\u002F2.licensing-failures",{"title":124,"path":125,"stem":126,"children":127,"page":25},"Reference","\u002Freference","9.reference",[128,132],{"title":129,"path":130,"stem":131},"Helm values","\u002Freference\u002Fhelm-values","9.reference\u002F1.helm-values",{"title":133,"path":134,"stem":135},"Environment variables","\u002Freference\u002Fenvironment","9.reference\u002F2.environment",{"id":137,"title":14,"body":138,"description":433,"extension":434,"links":435,"meta":436,"navigation":437,"path":15,"seo":438,"stem":16,"__hash__":439},"docs\u002F1.getting-started\u002F2.architecture.md",{"type":139,"value":140,"toc":419},"minimark",[141,145,170,173,177,183,186,206,213,216,222,225,231,251,254,257,264,267,281,284,288,297,324,334,343,347,361,364,374,380,384,387,400,404],[142,143,14],"h1",{"id":144},"architecture",[146,147,148,149,153,154,157,158,161,162,165,166,169],"p",{},"One ",[150,151,152],"code",{},"helm install"," renders the whole stack into one namespace. Resource names are prefixed with your release name and the chart name, so a release called ",[150,155,156],{},"my-mvno"," gets ",[150,159,160],{},"my-mvno-platform-api",", ",[150,163,164],{},"my-mvno-platform-license-agent",", and a Secret called ",[150,167,168],{},"my-mvno-platform-env",".",[146,171,172],{},"Here is what comes up, and why.",[174,175,176],"h2",{"id":176},"api",[146,178,179,180,169],{},"The platform binary, as a Deployment and a Service. This is the whole product surface: catalog, orders, SIMs, customers, billing, admin. It is the MVNE or the MVNO binary depending on ",[150,181,182],{},"platform.kind",[146,184,185],{},"It does not start serving immediately. Two initContainers gate it:",[187,188,189,200],"ul",{},[190,191,192,195,196,199],"li",{},[150,193,194],{},"wait-for-auth"," waits for GoTrue to create its ",[150,197,198],{},"auth.users"," table, because the API's own boot migrations have a foreign key onto it.",[190,201,202,205],{},[150,203,204],{},"wait-for-lease"," blocks until the license agent has written a lease into the platform Secret. An unlicensed platform never serves. This gate is unconditional and cannot be turned off.",[146,207,208,209,212],{},"Once running, the API watches the lease file at ",[150,210,211],{},"\u002Fetc\u002Fplatform\u002Flease",". When the agent renews or rotates the lease, the API picks up the new one without a restart.",[174,214,215],{"id":215},"auth",[146,217,218,219,221],{},"GoTrue, the auth realm for your deployment. It owns users, sessions, passkeys, magic links, and any social login providers you enable. It runs against the same Postgres as the API, in its own ",[150,220,215],{}," schema.",[174,223,224],{"id":224},"app",[146,226,227,228,230],{},"The web tier: an operator console for an MVNE, a customer-facing storefront for an MVNO. The image derives from ",[150,229,182],{},", so you do not name it.",[146,232,233,234,238,239,242,243,246,247,250],{},"It is ",[235,236,237],"strong",{},"off by default"," (",[150,240,241],{},"app.enabled: false","). Turn it on with ",[150,244,245],{},"--set app.enabled=true",". Give it its own hostname with ",[150,248,249],{},"ingress.hosts.app"," if you want it reachable from a browser, because the app Ingress only renders when both the workload is enabled and a host is set. Without a host it stays reachable inside the cluster only.",[174,252,253],{"id":253},"ess",[146,255,256],{},"The email sender. It consumes email jobs off Kafka, renders the template in-process, and sends through Resend. Order confirmations, eSIM delivery, password resets, and the rest of the transactional mail all flow through it.",[146,258,259,260,263],{},"Off by default (",[150,261,262],{},"ess.enabled: false","), and it needs Kafka wired up. With no brokers, it does not render at all.",[174,265,266],{"id":266},"realtime",[146,268,269,270,273,274,276,277,280],{},"An SSE sidecar that fans Kafka events out to connected browsers, which is what makes the console and storefront update live instead of on refresh. Off by default (",[150,271,272],{},"realtime.enabled: false","), and like ",[150,275,253],{}," it needs Kafka and its own hostname (",[150,278,279],{},"ingress.hosts.realtime",") to be publicly reachable.",[146,282,283],{},"Nothing breaks without it. The web tier falls back to normal request\u002Fresponse.",[174,285,287],{"id":286},"postgres-redis-kafka","postgres, redis, kafka",[146,289,290,291,293,294,296],{},"The data services. Postgres holds everything durable. Redis is the cache and the job queue. Kafka carries domain events, which is what ",[150,292,253],{}," and ",[150,295,266],{}," consume.",[146,298,299,300,303,304,307,308,311,312,315,316,319,320,323],{},"Postgres and Kafka default to ",[150,301,302],{},"mode: operator",": the chart renders a CloudNativePG ",[150,305,306],{},"Cluster"," for Postgres and a Strimzi ",[150,309,310],{},"KafkaNodePool"," plus ",[150,313,314],{},"Kafka"," for Kafka, and the corresponding operator (a cluster-scoped prerequisite you install once, see ",[317,318,18],"a",{"href":19},") reconciles them into a running, multi-replica-capable instance. Both also take ",[150,321,322],{},"mode: external"," to point at a managed service instead (RDS, Confluent, whatever you already run), which means you do not need the operator in the cluster at all.",[146,325,326,327,330,331,333],{},"Redis defaults to ",[150,328,329],{},"mode: bundled",": a single-node StatefulSet the chart runs itself, no operator involved. It is a cache rather than durable state, so there is no Redis operator worth adding as a prerequisite. ",[150,332,322],{}," points it at a managed cache if you need the cache itself to survive a node failure.",[146,335,336,337,339,340,342],{},"See ",[317,338,53],{"href":54}," for the values that matter for each mode, and ",[317,341,107],{"href":108}," for what to run in production.",[174,344,346],{"id":345},"the-license-agent","the license agent",[146,348,349,350,353,354,357,358,360],{},"A single-replica Deployment with a ",[150,351,352],{},"Recreate"," update strategy. On first install it generates the deployment's signing key inside your cluster, enrolls against ",[150,355,356],{},"activation.parentUrl"," using your activation code, and writes the resulting lease into the platform Secret. That write is what releases the API's ",[150,359,204],{}," gate.",[146,362,363],{},"It does not exit. It stays running for the life of the release, renewing the lease before it expires and rotating the key on schedule. If it stops, the deployment keeps serving until the current lease runs out, and then it stops serving.",[146,365,366,367,293,370,373],{},"Its RBAC is deliberately small: a ServiceAccount, a Role, and a RoleBinding that grant ",[150,368,369],{},"get",[150,371,372],{},"patch"," on exactly one Secret, by name. It cannot read anything else in your namespace and it cannot touch anything in any other namespace.",[146,375,376,377,379],{},"Do not scale it past one replica. A second replica is not supported: it is a second writer against the same license, and it raises a false alarm on an otherwise healthy deployment. ",[317,378,107],{"href":108}," has the detail.",[174,381,383],{"id":382},"the-secret-and-the-configmap","the Secret and the ConfigMap",[146,385,386],{},"The Secret carries every generated or derived credential: the JWT secret, the age encryption keypair, the database and cache connection strings, and the licensing keys the agent writes. The ConfigMap carries the non-secret environment.",[146,388,389,390,393,394,397,398,169],{},"The chart generates the Secret's values on first install and reuses them on every ",[150,391,392],{},"helm upgrade"," by reading the live Secret back, so an upgrade never rotates a password a running deployment depends on. If your secrets come from SOPS, sealed-secrets, or External Secrets, point ",[150,395,396],{},"secrets.existingSecret"," at your own and the chart renders none of its own. See ",[317,399,57],{"href":58},[174,401,403],{"id":402},"ingress","Ingress",[146,405,406,407,410,411,293,413,415,416,418],{},"One Ingress for the API, always, on ",[150,408,409],{},"platform.host",". One each for ",[150,412,224],{},[150,414,266],{}," when those are enabled and given their own hostname. All of them are nginx plus cert-manager, with no GKE Gateway and no cloud load-balancer assumptions. ",[317,417,40],{"href":41}," covers what to point where.",{"title":420,"searchDepth":421,"depth":422,"links":423},"",1,2,[424,425,426,427,428,429,430,431,432],{"id":176,"depth":422,"text":176},{"id":215,"depth":422,"text":215},{"id":224,"depth":422,"text":224},{"id":253,"depth":422,"text":253},{"id":266,"depth":422,"text":266},{"id":286,"depth":422,"text":287},{"id":345,"depth":422,"text":346},{"id":382,"depth":422,"text":383},{"id":402,"depth":422,"text":403},"What a single helm install of the platform chart brings up in your namespace, and what each piece does.","md",null,{},true,{"title":14,"description":433},"uqL5YKF49hSmqx9tfbFcyAvgXVSEyJwrglnKaaf9hCg",[441,443],{"title":10,"path":11,"stem":12,"description":442,"children":-1},"The difference between an MVNE and an MVNO, and how your deployment relates to the Solvegio Hub.",{"title":18,"path":19,"stem":20,"description":444,"children":-1},"The cluster, the four operators, DNS, image pull access, and an activation code you need before you run helm install.",1783967741116]