Railshot
AI deployment & operations platform
Implantar um app web criado localmente em ambientes de nuvem e on-premises preparados e depois operá-lo.
Uma plataforma de IA de implantação e operação que leva um app web criado localmente a ambientes de nuvem ou on-premises preparados e continua a operá-lo.
Os clusters de operações e de apps de clientes são separados, e o caminho público fica à parte do controle de implantação.
No K3s de operações, o painel, a API do produto e o build controller ficam em nós com papel platform e os Jobs runner em um nó com papel build, enquanto o Argo CD cuida da implantação. Os apps de clientes rodam em um namespace designado de um cluster K3s + Cilium de nó único, separado para cada destino registrado.
Build → publicação da imagem
A API do produto pede uma execução do GitHub Actions, e o runner do nó build pega e verifica o job. Só um bundle que passou nas mesmas verificações é publicado no GHCR por um job de release hospedado no GitHub.
API do produto · cd.js → bridge.py
railshot-systemexecutar · aplicar: workflow_dispatchloop job · self-hosted runnerrailshot-deploy.ymlA API do produto pede uma execução do GitHub Actions com tenant, app, commit de origem e destino. A API nunca chama um Job runner diretamente.
build controller · CronJob
railshot-systemexecutar · aplicar: repor Jobs runnerrunner Job · hostNetwork · Docker socketrailshot-buildO CronJob do build controller (a cada minuto, em um nó platform) cria Jobs runner no namespace railshot-build. Seu Role só permite get, list e create em Jobs, mais get, update e patch no único Secret de registro do runner; ele não pode criar namespaces nem outros Secrets.
runner Job · hostNetwork · Docker socket
railshot-buildexecutar · aplicar: pegar o jobloop job · self-hosted runnerrailshot-deploy.ymlO runner do nó com papel build (hostname exato, toleration NoSchedule) pega o job loop pelos rótulos self-hosted e o executa. O código do cliente roda nos contêineres restritos existentes de build Docker e verificação.
runner Job · hostNetwork · Docker socket
railshot-builddados: bundle testadorelease job · GitHub-hostedrailshot-deploy.ymlO job loop deixa o bundle que passou nas mesmas verificações, e o job de release só pega esse bundle.
release job · GitHub-hosted
railshot-deploy.ymldados: publicar por digestGHCRUm job de release hospedado no GitHub (ubuntu-24.04) publica no GHCR a imagem do bundle testado sem reconstruí-la. A publicação de imagens nunca acontece dentro do cluster de app do cliente.
Git de config → Argo → namespace do cliente
O Git de config carrega os recursos desejados e o GHCR os bytes da imagem. O Argo CD só aplica ao namespace do cliente registrado.
API do produto · cd.js → bridge.py
railshot-systemdados: workload commit · pushrepositório Git de configcd.js executa bridge.py, que só faz commit e push de workload.json quando o registro de publicação e o destino registrado coincidem, e depois relê a revisão remota. O Git carrega os recursos desejados; o GHCR carrega os bytes da imagem.
repositório Git de configdados: revisionArgo CD · Application
argocdA Application só lê seu repositório de config, caminho e revisão restritos.
API do produto · cd.js → bridge.py
railshot-systemexecutar · aplicar: Application syncArgo CD · ApplicationargocdA Application nomeada é definida por server-side apply, e uma sync de uma revisão exata é pedida sem prune. O ServiceAccount railshot-product da API do produto tem ao mesmo tempo o bootstrap de registro instalado pelo operador (create em Secrets, AppProjects e Applications no argocd; get, update e escalate em Roles nomeados) e permissões get e patch acrescentadas por nome exato para cada destino validado. O create do Kubernetes não pode ser limitado por nome, então a validação de entradas do produto também mantém os nomes dentro do escopo.
Argo CD · Application
argocdexecutar · aplicar: aplicar ao namespace<app namespace>O Argo CD aplica ao namespace designado no cluster do cliente com a credencial do Secret de cluster registrado. O AppProject só permite kinds de escopo de namespace e nenhum recurso de escopo de cluster.
Deployment · Pod · imagem por digest
<app namespace>dados: pull por digestGHCRA imagem publicada por digest é baixada com um Secret de pull referenciado pelo nome no mesmo namespace. Os valores dos Secrets nunca entram no artefato nem no Git de config.
Caminho das requisições públicas
As requisições ao app seguem ponto de entrada L7 do provedor → NodePort do nó → Service → Pod do app. A aplicação de políticas pelo Cilium é um controle separado, não um salto que carrega a requisição. O acesso ao painel de operações segue outro caminho.
Usuário do appdados: HTTPS 443Ponto de entrada L7 do provedor
Os usuários do app chegam ao ponto de entrada L7 do provedor pelo hostname registrado. Cada provedor implementa seu próprio ponto de entrada.
Ponto de entrada L7 do provedordados: NodePortNodePort Service · Local
<app namespace>O ponto de entrada encaminha ao endereço privado e ao NodePort do nó. O NodePort é o alocado no registro do destino.
NodePort Service · Local
<app namespace>dados: para o PodDeployment · Pod · imagem por digest<app namespace>O Service envia a requisição ao Pod do app (Deployment) no mesmo nó. Com externalTrafficPolicy Local, o endereço de origem chega inalterado à verificação de CIDRs registrados da NetworkPolicy.
Cilium · Helm · VXLAN
kube-systemexecutar · aplicar: aplicar a políticaNetworkPolicy · CIDRs listados<app namespace>O Cilium aplica a NetworkPolicy: entrada só a partir dos CIDRs registrados para a porta do app; nenhuma saída por padrão, exceto um endereço de banco de dados aprovado. Firewalls de nuvem e regras de host continuam necessários.
Usuário do Railshotdados: painel · APIdashboard · nginx
railshot-systemOs usuários entram pelo ponto de entrada de operações até o painel nginx, e o nginx fica na frente da API do produto. O Service do painel só é NodePort quando um node port aprovado é informado; a API continua ClusterIP.
Verificar · observar
O veredito de implantação se baseia só no estado do Argo e na verificação da URL pública. Diagnósticos de Pod, logs atuais e métricas de operação são sinais separados que nunca o mudam.
API do produto · cd.js → bridge.py
railshot-systemobservar: revisão · health · imagensArgo CD · ApplicationargocdDeployed exige a revisão de sync da Application, Synced e Healthy, a operação Succeeded e que as referências de imagem registradas no status da Application do Argo sejam iguais às imagens por digest do Deployment revisado. Não é um portão direto sobre o imageID do Pod, e um status verde do Argo sozinho não estabelece a correspondência.
API do produto · cd.js → bridge.py
railshot-systemobservar: verificação HTTPS públicaPonto de entrada L7 do provedorA URL pública registrada é verificada sem redirecionamentos. Succeeded só é registrado com o veredito do Argo e esta verificação.
API do produto · cd.js → bridge.py
railshot-systemobservar: Diagnóstico do Pod · fora do vereditoDeployment · Pod · imagem por digest<app namespace>Após o veredito do Argo e a verificação pública, o Deployment, os ReplicaSets e os Pods são lidos separadamente com a credencial registrada, e o imageID e a prontidão são acrescentados como diagnóstico ready, progressing ou unavailable. Um diagnóstico com falha ou divergente nunca muda o veredito deployed ou succeeded.
API do produto · cd.js → bridge.py
railshot-systemobservar: tail de logs · 100 linhas por PodDeployment · Pod · imagem por digest<app namespace>Só para a operação implantada no momento, com a config confiável e a credencial registrada. Para cada Pod atual (no máximo três), o digest da imagem em execução deve ser igual ao digest esperado; depois são lidas até 100 linhas com dados ocultados por Pod, dentro de um limite de bytes. Uma tentativa anterior responde superseded; sem configuração, not_configured. Não é agregação de logs.
VM de observaçãoobservar: coleta · NodePortsrailshot-observability
O Prometheus na VM de observação coleta node-exporter e kube-state-metrics por dois NodePorts restritos por firewall. Valores ausentes ou desatualizados são desconhecidos, não zero.
VM de observaçãoobservar: Blackbox probePonto de entrada L7 do provedor
probe_success=1 significa um 2xx naquele ponto de probe. Não prova o acesso de todos os usuários nem a versão implantada, e nunca promove published a deployed.
Instalação · registro pelo operador
A instalação do cluster, o bootstrap de registro e as entradas do destino, os Secrets e a configuração de renovação de credenciais são entradas do operador, nunca parte do payload do app do tenant.
Bootstrap do operadorexecutar · aplicar: renderizar a plataforma · Secretrailshot-system
O operador gera a declaração da plataforma com render-platform.py e fornece a config de operações privada e os Secrets. O MCP remoto só é incluído quando sua imagem é fornecida, e o gateway pessoal (WireGuard) só com --personal.
Bootstrap do operadorexecutar · aplicar: bootstrap de registroAppProject · cluster Secret
argocdO operador instala no argocd o Role de bootstrap de registro e um Role de registros vazio, e prepara as entradas do destino. Os helpers de registro que a API do produto executa (environment.py, applications.py) gravam então o Secret railshot-<target> com uma credencial de escopo limitado (clusterResources false, namespaces permitidos listados) e acrescentam os nomes exatos validados ao Role de registros. O registro por app vincula um namespace app-<id> a um AppProject de mesmo nome.
Bootstrap do operadorexecutar · aplicar: instalar K3s · CiliumCilium · Helm · VXLAN
kube-systeminstall-k3s.sh instala um servidor K3s de nó único (flannel, network policy, traefik e servicelb desligados; kube-proxy mantido), e o Cilium é instalado como release do Helm (kubeProxyReplacement false, VXLAN, Hubble desligado).
CronJob de renovação · a cada 2 h
argocdexecutar · aplicar: TokenRequest · 6 hSA railshot-argocd · token<app namespace>O CronJob railshot-credentials pede a cada duas horas um token novo de seis horas para a SA do cliente. O Role do lado do cliente só permite criar token para a própria SA. Seis horas é o TTL de um token, não o momento em que o acesso do operador termina.
Todas as conexões com a mesma espessura. Escolha um fluxo para destacar só o caminho dele, com etapas numeradas.
- Contínua · direção em que fluem requisições, bundles, imagens e declarações
- Tracejada · pedidos de execução, aplicação, instalação, credenciais
- Traço longo e ponto · leitura de estado, logs, probes, métricas; separada do veredito
- Linha preta grossa · o fluxo selecionado; os números dão a ordem
- Caixa · um namespace, com o nome no cabeçalho (não um nó)
- Chip interno · componente desse namespace
- Caixa tracejada · fora dos clusters
- railshot-system · produto
- argocd · autoridade de implantação
- railshot-build · autoridade de Docker do host
- GitHub · cluster do cliente
A decisão de alocação de October 2 em docs/architecture (dois nós de papel) e o diagrama do README público são histórico; este mapa segue as declarações e o código de 645e28a. As setas mostram causa e direção, não a prova de que cada caminho de provedor está ativo agora.
- Namespaces e nós
- Um namespace é um escopo de nomes: nem hardware nem, por si só, uma fronteira de segurança. Os papéis platform e build são uma alocação de scheduling por nodeSelector e taint; este mapa não fixa o número de nós do cluster de operações. A alocação de nós do Argo CD também é uma verificação separada.
- Autoridade do nó build
- Os Jobs runner rodam com hostNetwork, o socket Docker do host, NET_ADMIN e root. Namespace, taint ou RBAC não removem a autoridade de Docker do host, então isso não é isolamento em relação ao administrador da VM nem sandbox para código arbitrário.
- Namespace do cliente
- Cada destino registrado usa um namespace e um AppProject não padrão (por exemplo tenant-demo, ou app-<id> por app). Só são criados o Deployment de um serviço (montagem automática do token de ServiceAccount desligada), um Service NodePort e uma NetworkPolicy, mais um PVC ou Job de migração opcionais; os Secrets só são referenciados pelo nome (o Secret de pull e um DATABASE_URL aprovado), nunca criados.
- Entrada da AWS
- Alias do Route 53 → ALB compartilhado HTTPS 443 → endereço privado e NodePort do nó do cliente. Vários apps reutilizam um mesmo nó, cada um com seu namespace, Application e NodePort.
- Entrada do GCP
- LB HTTPS global EXTERNAL_MANAGED → NEG zonal GCE_VM_IP_PORT → NodePort privado da VM existente. Os certificados vêm do Certificate Manager com autorização DNS. O módulo não é dono da VM nem da VPC, e uma única VM continua sendo ponto único de falha.
- Entrada do OpenStack
- Listener TLS do Amphora 443 com políticas L7 de Host e path → NodePort HTTP. O endereço padrão é uma VIP privada; Octavia, K3s, o banco de dados e o DNS ficam fora do módulo. A acessibilidade pública e a prontidão do Amphora no local exigem aceitação separada, e nenhuma aplicação no local foi verificada ainda com este módulo.
- Entre provedores
- As três entradas são implementações distintas, sem failover nem replicação automáticos entre provedores. O cluster de app do cliente é um exemplo de destino registrado, não a afirmação de que a mesma implantação roda em todas as nuvens ao mesmo tempo.
- Opções do operador
- O gateway pessoal (WireGuard) só entra na plataforma quando o operador ativa --personal; não é um caminho obrigatório para as nuvens de clientes. No modo direct suportado, a VM de observação pode compartilhar a VM de controle de operações.