Deploy-Fehler haben keine Symptome. Vier Leitplanken — getrennte Vaults, EDITOR-Skripte, Rückleseprüfung, Laufzeit-Flip per env — halten Claude nützlich, ohne dass alles brennt.
Der große Unterschied zwischen Deployen und Code schreiben: Deployment ist ein Schuss, hohes Risiko, schmerzhaft rückgängig zu machen. Schreib eine kaputte Zeile Code, ein Testlauf fängt sie. Schreib eine kaputte Zeile Credentials, und du merkst es erst bei der ersten echten Zahlung — User haben ihre Karte belastet, das Geld ist nicht auf deinem Konto, die Logs sind nichts als 400.
Ich habe how2claude kürzlich mit Claude vom lokalen Dev in die Produktion gebracht: Stripe-Live-Konto, x402-Mainnet-Wallet, Google OAuth, Kamal Secrets. Claude weiß nicht, welche Werte Test und welche echt sind. Es weiß nicht, dass sk_live_ vs sk_test_ ein Ein-Buchstaben-Unterschied mit weltuntergangsartigen Konsequenzen ist. Die Leitplanken baust du.
Rails kommt per Default mit config/credentials.yml.enc, entschlüsselt durch config/master.key. Wenn du diesen Default nimmst, hast du verloren.
Wenn du sk_live_xxx in diese Datei schreibst und dann lokale Tests fährst, ruft dein Testcode Produktions-Stripe auf. Jeder Testlauf belastet eine echte Karte.
Teile in zwei:
- config/credentials.yml.enc + config/master.key: dev/test, hält sk_test_xxx
- config/credentials/production.yml.enc + config/credentials/production.key: prod, hält sk_live_xxx
Beide .key in .gitignore, beide .enc committen. production.key lebt nur auf der Deploy-Maschine.
Dann müssen die Kamal Secrets auf die richtige Datei zeigen:
# .kamal/secrets
-RAILS_MASTER_KEY=$(cat config/master.key)
+RAILS_MASTER_KEY=$(cat config/credentials/production.key)
Im Container zeigt RAILS_MASTER_KEY jetzt auf den Produktionsschlüssel, und was entschlüsselt wird, sind die Produktions-Credentials. Ich habe Claude diese Zeile schreiben sehen — das Kamal-Default-Template ist config/master.key (das Dev), das den Dev-Stripe-Key still und leise in die Produktion deployt.
bin/rails credentials:edit --environment production öffnet einen interaktiven Editor. Claude kann keinen interaktiven Editor steuern. Du willst auch nicht manuell ein Dutzend Secrets einfügen (ein Tippfehler und alles ist hin).
Nutze dieses Muster:
EDITOR="ruby script/set_prod_webhook_secret.rb" \
bin/rails credentials:edit --environment production
rm script/set_prod_webhook_secret.rb
script/set_prod_webhook_secret.rb sieht so aus:
# ARGV[0] ist der Pfad zur temporären entschlüsselten YAML-Datei
file = ARGV[0]
require "yaml"
data = YAML.load_file(file) || {}
data["stripe"] ||= {}
data["stripe"]["webhook_secret"] = "whsec_GHWObNAKFh2HPOlJpbGmlYfIiKz1C8EY"
File.write(file, data.to_yaml)
Rails schreibt das entschlüsselte YAML in eine Temp-Datei, ruft deinen „EDITOR" mit diesem Pfad als Argument auf und verschlüsselt beim Beenden neu. Dein „EDITOR" ist einfach ein Ruby-Skript, das chirurgisch einen Key ändert und speichert.
Warum das gut ist:
- Präzise: berührt nur stripe.webhook_secret, sonst nichts.
- Idempotent: zweimal ausführen ist wie einmal.
- Auditierbar: das Skript ist der Diff. Claude schreibt es, du schaust kurz drauf, und weißt genau, was es ändert.
- Verschwindet beim Löschen: nach rm gibt es kein Klartext-Credential auf der Platte, kein whsec_...-Paste in der Shell-History.
Ein Skript pro Credential: set_stripe_live_key.rb, set_webhook_secret.rb, set_price_ids.rb, set_wallet_address.rb. Jedes gleich danach rm.
Nach dem Schreiben trau nicht darauf, dass er's richtig geschrieben hat. Lies zurück:
bin/rails runner -e production "
c = Rails.application.credentials
puts 'sk_live set: ' + c.dig(:stripe, :secret_key).to_s.start_with?('sk_live_').to_s
puts 'webhook_secret set: ' + c.dig(:stripe, :webhook_secret).to_s.start_with?('whsec_').to_s
puts 'wallet prefix: ' + c.dig(:x402, :wallet_address).to_s[0..5]
puts 'wallet len: ' + c.dig(:x402, :wallet_address).to_s.length.to_s
"
Der Schlüssel sind Präfix- und Längen-Checks, nicht nur die Werte ausdrucken.
sk_live_. Liest du sk_test_ zurück, hat Claude den Test-Key in die Prod-Datei gesteckt — ein Bug, den du erst bei der ersten echten Zahlung findest.whsec_. Format stimmt oder nicht, mit einem Blick zu sehen.0x.... Falsche Länge bedeutet, ein anderes Zeichen hat sich eingeschlichen.Präfix-Checks hinzufügen blockiert die meisten Tippfehler, vergessene Felder und gemischte Umgebungen.
Es ist verlockend, zu denken „ich stelle das Netzwerk vor dem Deploy auf Mainnet um". So ein Switch hängt am menschlichen Gedächtnis. Früher oder später wird's dich verbrennen.
Back die Regel in den Initializer:
# config/initializers/x402.rb
X402.configure do |c|
c.wallet_address = Rails.application.credentials.dig(:x402, :wallet_address)
c.chain = Rails.env.production? ? "base" : "base-sepolia"
end
Derselbe Code, Testnet (sepolia) in Dev, Mainnet (base) in Prod. Beim Deploy nichts zu ändern. Claude kann nicht „vergessen umzuschalten", weil Umschalten nicht sein Job ist.
Gleicher Trick für basescan_tx_url, Plan-Sichtbarkeit (dev-only Pläne werden in Prod nicht gerendert), Stripe-Price-ID-Auswahl und so weiter. Alles, was sich zwischen Dev und Prod unterscheidet, über Rails.env kippen. Verlass dich nicht aufs Erinnern zur Deploy-Zeit.
Mit allen vier Leitplanken musst du trotzdem die Zahlung mit echtem Geld selbst durchklicken.
Im vorherigen Artikel Stille Bugs mit Claude debuggen habe ich den ersten x402-Zahlungsklick in Prod beschrieben: invalid_string at payTo in der Console. Das 43. Zeichen der Wallet-Adresse war ein Fullwidth-Fragezeichen, das aus einer chinesischen IME reingerutscht war. Präfix-Checks konnten es nicht fangen (0x stand da noch), Tests konnten es nicht fangen (Tests feuern keine echten Transaktionen ab), nur ein echter Klick hat es zutage gefördert.
Deployment-Leitplanken sind nicht für Claude. Sie sind für dich — automatisier, was sich automatisieren lässt (Präfixe, Längen, Env-Switches), damit die gesparte Aufmerksamkeit in die echten Interaktionen fließt, die Automation nicht abdeckt.
Der volle Claude-Deploy-Flow:
.enc- + .key-Dateien.EDITOR=script, lösch es sofort.Rails.env kippen, nicht zur Deploy-Zeit.Deployment ist nicht „Code schreiben mit höheren Einsätzen". Deployment ist „Code schreiben, bei dem dir niemand sagt, dass du falsch liegst". Die Leitplanken sind da, um „niemand sagt's dir" in „du weißt es binnen 15 Sekunden" zu verwandeln.