====== Cloud-Infrastruktur mit Lyx ======
Dieser Guide erklärt den praktischen Einsatz der ''std.cloud''-Units für AWS, DigitalOcean, Cloudflare und Google Cloud Platform: Credential-Setup, Response-Handling, typische Workflows und die Auswahl des richtigen Dienstes.
→ [[lyx_-_programmiersprache:units:cloud|std.cloud (Unit-Referenz)]] · [[lyx_-_programmiersprache:units:cloud:aws|AWS-Units]] · [[lyx_-_programmiersprache:units:cloud:do|DigitalOcean-Units]] · [[lyx_-_programmiersprache:units:cloud:cf|Cloudflare-Units]] · [[lyx_-_programmiersprache:units:cloud:gcp|GCP-Units]] · [[lyx_-_programmiersprache:guides:welche-unit|Welche Unit?]]
----
===== Credentials einrichten =====
==== AWS ====
AWS verwendet ''AWSCreds'' als zentralen Credential-Typ. Die empfohlene Methode ist die **Credential Chain** via ''AWSCredentialsDefault()'': Sie prüft automatisch Umgebungsvariablen, dann ''~/.aws/credentials''.
import std.cloud.aws.core;
// Empfohlen: Chain (Env → ~/.aws/credentials → leer)
var creds: AWSCreds := AWSCredentialsDefault();
// Explizit aus Umgebungsvariablen (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY):
var creds2: AWSCreds := AWSCredentialsFromEnv();
// Explizit aus ~/.aws/credentials (Profil "production"):
var creds3: AWSCreds := AWSCredentialsProfile("production"c);
// Hardcodiert (nur für Tests):
var creds4: AWSCreds := AWSCredentialsStatic("AKIAIOSFODNN7EXAMPLE"c,
"wJalrXUtnFEMI/K7MDENG/bPxRfi..."c);
// Am Ende immer freigeben:
AWSCredentialsFree(creds);
**Umgebungsvariablen für AWS:**
^ Variable ^ Bedeutung ^
| ''AWS_ACCESS_KEY_ID'' | Access Key ID |
| ''AWS_SECRET_ACCESS_KEY'' | Secret Access Key |
| ''AWS_SESSION_TOKEN'' | Session Token (nur bei STS/temporären Creds) |
| ''AWS_DEFAULT_REGION'' | Ziel-Region (z.B. ''eu-central-1'') |
| ''AWS_REGION'' | Alternativ zu ''AWS_DEFAULT_REGION'' |
==== DigitalOcean ====
DigitalOcean verwendet einen einfachen Bearer-Token:
import std.cloud.do.credentials;
// Aus Umgebungsvariable DIGITALOCEAN_TOKEN (empfohlen):
var creds: DOCredentials := DOCredentialsFromEnv();
// Direkt (nur für Tests):
var creds2: DOCredentials := DOCredentialsFromToken("dop_v1_xxxxxxxxxxxx"c);
// Aus doctl-Konfigurationsdatei (~/.config/doctl/config.yaml):
var creds3: DOCredentials := DOCredentialsDefault();
// Am Ende immer freigeben:
DOCredentialsFree(creds);
----
===== Response-Handling-Muster =====
Die meisten Lese-Funktionen in ''std.cloud'' geben ein ''int64'' zurück, das auf einen allokierten Puffer mit der **rohen API-Antwort** zeigt (XML bei AWS S3/EC2/SQS, JSON bei AWS DynamoDB/Lambda/DO). Bei Fehler oder leerem Ergebnis ist der Rückgabewert ''0''.
**Schreib- und Lösch-Funktionen** geben ''int64'' zurück: ''1'' = Erfolg, ''0'' = Fehler.
import std.cloud.aws.core;
import std.cloud.s3;
import std.alloc;
fn main(): int64 {
var creds: AWSCreds := AWSCredentialsDefault();
var s3: S3Client := S3Connect(creds, "eu-central-1"c);
// Lese-Funktion: gibt allokierten Puffer zurück (0 bei Fehler)
var result: int64 := S3ListBuckets(s3);
if (result == 0) {
PrintLn("Fehler oder keine Buckets"c);
S3Close(s3);
return 1;
}
Print(result as pchar);
PrintLn(""c);
free(result, StrLen(result as pchar) + 1); // Puffer freigeben!
// Schreib-Funktion: gibt 1 bei Erfolg zurück
var ok: int64 := S3PutObjectFile(s3, "mein-bucket"c,
"logs/2026-06-12.log"c,
"/var/log/app.log"c,
"text/plain"c);
if (ok == 0) {
PrintLn("Upload fehlgeschlagen"c);
}
S3Close(s3);
return 0;
}
**Jeder Rückgabepuffer muss freigegeben werden.** Die Cloud-Units allokieren den Response-Body mit ''alloc()'' — der Aufrufer trägt die Ownership. ''free(result, StrLen(result as pchar) + 1)'' ist das Standardmuster.
----
==== Cloudflare ====
Cloudflare verwendet ''CFCredentials'' mit API-Token. Der wichtigste Unterschied zu AWS und DO: Cloudflare gibt immer HTTP 200 zurück — der Erfolg steckt im ''success''-Feld der ''CFResponse''. Die High-Level-Funktionen (''DNSRecordUpsert'', ''KVGet'', …) abstrahieren das weg und geben direkt ''int64'' (Puffer oder 0) bzw. Struct zurück.
import std.cloud.cf.credentials;
import std.cloud.cf.transport;
// Aus Umgebungsvariablen CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID:
var creds: CFCredentials := CFCredentialsFromEnv();
var c: CFClient := CFClientNew(creds);
// Cleanup:
CFClientFree(c);
CFCredentialsFree(creds);
**Umgebungsvariablen für Cloudflare:**
^ Variable ^ Bedeutung ^
| ''CLOUDFLARE_API_TOKEN'' | API-Token (empfohlen) |
| ''CLOUDFLARE_ACCOUNT_ID'' | Account-ID |
| ''CLOUDFLARE_API_KEY'' | Legacy API-Key |
| ''CLOUDFLARE_API_EMAIL'' | Legacy E-Mail (nur zusammen mit API-Key) |
==== Google Cloud Platform ====
GCP verwendet ''GCPCredentials'' mit **Application Default Credentials (ADC)**. Die Credential Chain prüft drei Quellen automatisch: Umgebungsvariable → ADC-Datei → GCE-Metadata-Server.
import std.cloud.gcp.credentials;
// Empfohlen: Credential Chain
// 1. GOOGLE_APPLICATION_CREDENTIALS → JSON-Datei
// 2. ~/.config/gcloud/application_default_credentials.json
// 3. GCE-Metadata-Server (automatisch auf Compute Engine)
var creds: GCPCredentials := GCPCredentialsDefault(
"https://www.googleapis.com/auth/cloud-platform"c);
// Explizit aus Service-Account-JSON-Datei:
// var creds2: GCPCredentials := GCPCredentialsFromFile("/pfad/sa.json"c, scope);
GCPCredentialsFree(creds);
**Umgebungsvariablen für GCP:**
^ Variable ^ Bedeutung ^
| ''GOOGLE_APPLICATION_CREDENTIALS'' | Pfad zur Service-Account- oder Authorized-User-JSON-Datei |
Der Token wird intern gecacht und automatisch erneuert, sobald er weniger als 60 Sekunden gültig ist.
----
===== Typische Workflows =====
==== Workflow 1: Datei nach S3 hochladen und Presigned URL erzeugen ====
import std.cloud.aws.core;
import std.cloud.s3;
import std.alloc;
fn main(): int64 {
var creds: AWSCreds := AWSCredentialsDefault();
var s3: S3Client := S3Connect(creds, "eu-central-1"c);
// Kleine Datei hochladen
S3PutObjectFile(s3, "mein-bucket"c, "report.pdf"c,
"/tmp/report.pdf"c, "application/pdf"c);
// Presigned GET-URL (gültig 3600 Sekunden = 1 Stunde)
var url: int64 := S3PresignGet(s3, "mein-bucket"c, "report.pdf"c, 3600);
if (url != 0) {
Print("Download-Link: "c); Print(url as pchar); PrintLn(""c);
free(url, StrLen(url as pchar) + 1);
}
// Presigned PUT-URL (damit Clients direkt uploaden können, ohne Credentials)
var putUrl: int64 := S3PresignPut(s3, "mein-bucket"c, "uploads/foto.jpg"c, 900);
if (putUrl != 0) {
Print("Upload-Link: "c); Print(putUrl as pchar); PrintLn(""c);
free(putUrl, StrLen(putUrl as pchar) + 1);
}
// Fehler prüfen
if (S3LastStatus(s3) != 200 && S3LastStatus(s3) != 0) {
Print("Fehler: "c); Print(S3LastError(s3)); PrintLn(""c);
}
S3Close(s3);
return 0;
}
**S3-kompatible Dienste** — dieselbe API, anderer Connect:
// Cloudflare R2 (kein Egress-Preis):
var r2: S3Client := S3ConnectR2("mein-account-id"c,
"r2-access-key"c, "r2-secret-key"c);
// MinIO (lokale Entwicklung / Self-Hosted):
var minio: S3Client := S3ConnectMinio("localhost"c, 9000,
"minioadmin"c, "minioadmin"c);
// Nach dem Connect identische API wie AWS S3:
S3PutObjectFile(r2, "mein-bucket"c, "datei.pdf"c, "/tmp/datei.pdf"c, "application/pdf"c);
S3Close(r2);
==== Workflow 2: Secret aus Secrets Manager lesen ====
Das häufigste Muster für Produktions-Deployments: Credentials nie in den Code einbetten, immer aus Secrets Manager lesen.
import std.cloud.aws.core;
import std.cloud.secrets;
import std.alloc;
fn main(): int64 {
var creds: AWSCreds := AWSCredentialsDefault();
var sm: SecretsClient := SecretsConnect(creds, "eu-central-1"c);
// Direkt als String (häufigster Fall)
var dbPassword: int64 := SecretsGetString(sm, "prod/db/password"c);
if (dbPassword == 0) {
PrintLn("Secret nicht gefunden"c);
SecretsClose(sm);
return 1;
}
// ... Secret verwenden ...
// Sofort nach Verwendung aus dem Speicher löschen:
var n: int64 := StrLen(dbPassword as pchar);
var i: int64 := 0;
while (i < n) { poke8(dbPassword + i, 0); i := i + 1; }
free(dbPassword, n + 1);
SecretsClose(sm);
return 0;
}
==== Workflow 3: Nachrichten über SQS verarbeiten ====
import std.cloud.aws.core;
import std.cloud.sqs;
import std.alloc;
fn main(): int64 {
var creds: AWSCreds := AWSCredentialsDefault();
var sqs: SQSClient := SQSConnect(creds, "eu-central-1"c);
var queueUrl: pchar := "https://sqs.eu-central-1.amazonaws.com/123456789/meine-queue"c;
// Long Polling: bis zu 5 Nachrichten, 20 Sekunden warten
var xml: int64 := SQSReceiveMessages(sqs, queueUrl, 5, 20);
if (xml != 0) {
// Erste Nachricht parsen
var msg: SQSMessage := SQSParseFirstMessage(xml, StrLen(xml as pchar));
// Nachricht verarbeiten
Print("Body: "c); Print(msg.body as pchar); PrintLn(""c);
// Quittieren (sonst wird sie nach VisibilityTimeout erneut zugestellt)
SQSDeleteMessage(sqs, queueUrl, msg.receiptHandle as pchar);
SQSMessageFree(msg);
free(xml, StrLen(xml as pchar) + 1);
}
SQSClose(sqs);
return 0;
}
==== Workflow 4: Lambda-Funktion aufrufen ====
import std.cloud.aws.core;
import std.cloud.lambda;
import std.alloc;
fn main(): int64 {
var creds: AWSCreds := AWSCredentialsDefault();
var lc: LambdaClient := LambdaConnect(creds, "eu-central-1"c);
var payload: pchar := "{\"action\":\"process\",\"id\":42}"c;
var payloadLen: int64 := StrLen(payload);
// Synchroner Aufruf (RequestResponse) — gibt Response-Body zurück
var resp: int64 := LambdaInvoke(lc, "meine-funktion"c,
payload as int64, payloadLen);
if (resp != 0) {
Print("Response: "c); Print(resp as pchar); PrintLn(""c);
free(resp, StrLen(resp as pchar) + 1);
}
LambdaClose(lc);
return 0;
}
==== Workflow 5: DigitalOcean Droplet erstellen und warten ====
import std.cloud.do.credentials;
import std.cloud.do.droplets;
import std.alloc;
fn main(): int64 {
var creds: DOCredentials := DOCredentialsFromEnv();
// SSH-Key-ID muss vorher im DO-Account hinterlegt sein
var keyIds: array := [12345678];
var d: DODroplet := DropletCreateSimple(
creds,
"web-01"c, // Name
"fra1"c, // Region: Frankfurt
"s-2vcpu-4gb"c, // Größe
"ubuntu-22-04-x64"c, // Image-Slug
keyIds as int64,
1 // Anzahl
);
if (d.id == 0) {
PrintLn("Droplet-Erstellung fehlgeschlagen"c);
DOCredentialsFree(creds);
return 1;
}
// Polling bis Droplet "active" ist (blockiert bis zu ~60 s)
d = DropletWait(creds, d.id, "active"c);
var ip: int64 := DropletGetIP(creds, d.id);
if (ip != 0) {
Print("Droplet bereit unter: "c);
Print(ip as pchar);
PrintLn(""c);
free(ip, StrLen(ip as pchar) + 1);
}
DODropletFree(d);
DOCredentialsFree(creds);
return 0;
}
==== Workflow 6: Cloudflare DNS-Record setzen ====
import std.cloud.cf.credentials;
import std.cloud.cf.transport;
import std.cloud.cf.dns;
fn main(): int64 {
var creds: CFCredentials := CFCredentialsFromEnv();
var c: CFClient := CFClientNew(creds);
// Upsert: legt den Record an oder aktualisiert ihn falls vorhanden
var r: CFDNSRecord := DNSRecordUpsert(c,
"abc123def456"c, // Zone-ID (aus Cloudflare-Dashboard)
"A"c, // Typ
"api"c, // Name → api.meine-domain.de
"1.2.3.4"c, // Content
1, // TTL (1 = auto)
1 // proxied (1 = oranges Wolkensymbol)
);
CFDNSRecordFree(r);
CFClientFree(c);
CFCredentialsFree(creds);
return 0;
}
==== Workflow 7: Cloudflare Worker deployen ====
import std.cloud.cf.credentials;
import std.cloud.cf.transport;
import std.cloud.cf.workers;
import std.fs;
import std.alloc;
fn main(): int64 {
var creds: CFCredentials := CFCredentialsFromEnv();
var c: CFClient := CFClientNew(creds);
// Worker-Script aus Datei laden
var sz: int64 := FileSize("worker.js"c);
var script: int64 := alloc(sz + 1);
ReadFile("worker.js"c, script as pchar, sz);
poke8(script + sz, 0);
// Deployen
var ok: int64 := WorkerDeploy(c, creds.accountId as pchar,
"mein-worker"c, script, sz);
free(script, sz + 1);
if (ok == 1) {
PrintLn("Worker deployed"c);
}
// Route anlegen: meine-domain.de/api/* → Worker
var route: CFWorkerRoute := WorkerRouteCreate(c,
"abc123def456"c, // Zone-ID
"meine-domain.de/api/*"c,
"mein-worker"c
);
CFWorkerRouteFree(route);
CFClientFree(c);
CFCredentialsFree(creds);
return 0;
}
==== Workflow 8: R2-Objekt hochladen ====
import std.cloud.cf.credentials;
import std.cloud.cf.r2;
import std.fs;
import std.alloc;
fn main(): int64 {
var creds: CFCredentials := CFCredentialsFromEnv();
var r2: R2Conn := R2Connect(creds, creds.accountId as pchar);
// Datei hochladen
var sz: int64 := FileSize("/tmp/export.csv"c);
var data: int64 := alloc(sz);
ReadFile("/tmp/export.csv"c, data as pchar, sz);
R2Upload(r2, "mein-bucket"c, "exports/2026-06-12.csv"c, data, sz);
free(data, sz);
// Presigned URL für 1 Stunde
var url: int64 := R2PresignedURL(r2, "mein-bucket"c,
"exports/2026-06-12.csv"c, "GET"c, 3600);
if (url != 0) {
Print("Download: "c); Print(url as pchar); PrintLn(""c);
free(url, StrLen(url as pchar) + 1);
}
R2Disconnect(r2);
CFCredentialsFree(creds);
return 0;
}
==== Workflow 9: Cache nach Deployment leeren ====
import std.cloud.cf.credentials;
import std.cloud.cf.transport;
import std.cloud.cf.cache;
fn purgeAfterDeploy(zoneId: pchar): void {
var creds: CFCredentials := CFCredentialsFromEnv();
var c: CFClient := CFClientNew(creds);
// Gesamten Zone-Cache leeren
CachePurgeAll(c, zoneId);
CFClientFree(c);
CFCredentialsFree(creds);
}
==== Workflow 10: CloudWatch-Metrik schreiben ====
import std.cloud.aws.core;
import std.cloud.cloudwatch;
import std.alloc;
fn reportMetric(value: int64): void {
var creds: AWSCreds := AWSCredentialsDefault();
var cw: CWClient := CWConnect(creds, "eu-central-1"c);
// datum als JSON-String mit einem einzelnen Datenpunkt
var datum: pchar := "[{\"MetricName\":\"RequestCount\",\"Value\":1,\"Unit\":\"Count\"}]"c;
CWPutMetricData(cw, "MeineApp"c, datum as int64, 1);
CWClose(cw);
AWSCredentialsFree(creds);
}
==== Workflow 11: GCS-Datei hochladen und herunterladen ====
import std.cloud.gcp.credentials;
import std.cloud.gcp.storage;
import std.alloc;
fn main(): int64 {
var creds: GCPCredentials := GCPCredentialsDefault(
"https://www.googleapis.com/auth/devstorage.read_write"c);
var gcs: GCSConn := GCSConnect(addr creds, "mein-projekt"c);
// Datei hochladen
var data: pchar := "Reporting-Daten 2026"c;
GCSUpload(gcs, "mein-bucket"c, "reports/2026-06.txt"c,
data as int64, 20, "text/plain"c);
// Datei herunterladen — Body liegt im GCPResponse
var r: GCPResponse := GCSDownload(gcs, "mein-bucket"c, "reports/2026-06.txt"c);
if (r.statusCode == 200) {
Print(r.body as pchar); PrintLn(""c);
}
GCPResponseFree(r); // gibt r.body und r.contentType frei
GCSDisconnect(gcs);
GCPCredentialsFree(creds);
return 0;
}
**GCP-spezifisch:** Alle Lese-Funktionen geben ''GCPResponse'' zurück (nicht ''int64''). Immer ''GCPResponseFree(r)'' statt ''free(r.body, …)'' verwenden — das gibt Body und Content-Type gemeinsam frei.
==== Workflow 12: GCP-Secret auslesen ====
import std.cloud.gcp.credentials;
import std.cloud.gcp.secrets;
import std.alloc;
fn main(): int64 {
var creds: GCPCredentials := GCPCredentialsDefault(
"https://www.googleapis.com/auth/cloud-platform"c);
var sc: SecretConn := SecretConnect(addr creds, "mein-projekt"c);
// Secret anlegen (base64url-kodiert gespeichert)
var payload: pchar := "geheimes-passwort-42"c;
SecretCreate(sc, "db-password"c, payload as int64, 20);
// Neueste Version auslesen — wird automatisch dekodiert
var val: int64 := SecretGet(sc, "db-password"c);
if (val != 0) {
// ... verwenden ...
// Sofort überschreiben und freigeben
var n: int64 := StrLen(val as pchar);
var i: int64 := 0;
while (i < n) { poke8(val + i, 0); i := i + 1; }
free(val, n + 1);
}
SecretDisconnect(sc);
GCPCredentialsFree(creds);
return 0;
}
----
===== Welchen AWS-Dienst wählen? =====
==== Speicher ====
^ Anforderung ^ Dienst ^ Begründung ^
| Dateien, Backups, statische Assets | ''std.cloud.s3'' | Objekt-Speicher; unbegrenzte Kapazität; Presigned URLs |
| Strukturierte Daten mit schnellem Lookup | ''std.cloud.dynamodb'' | Schemalos; millisekunden-Latenz bei gezieltem Key-Zugriff |
| Relationale Daten | Managed DB (extern) | Kein RDS in std.cloud — eigene Instanz via EC2 + ''std.db'' |
| Temporäre Konfiguration / Feature Flags | ''std.cloud.secrets'' SSM | SSM Parameter Store für nicht-sensitive Konfiguration |
| Geheimnisse (Passwörter, API-Keys) | ''std.cloud.secrets'' Secrets Manager | Automatische Rotation, Audit-Log in CloudTrail |
==== Messaging & Events ====
^ Anforderung ^ Dienst ^ Begründung ^
| Aufgaben-Queue (Worker verarbeiten Jobs) | ''std.cloud.sqs'' | Pull-basiert; Retry via VisibilityTimeout; Dead-Letter-Queue |
| Benachrichtigungen an mehrere Empfänger | ''std.cloud.sns'' | Push-basiert; Fan-out an SQS, E-Mail, SMS, HTTPS gleichzeitig |
| Bestimmte Reihenfolge garantieren | ''std.cloud.sqs'' FIFO | FIFO-Queue mit MessageGroupId |
| E-Mail versenden | ''std.cloud.sns'' (PublishToPhone) | Nur SMS; für E-Mail: SNS + E-Mail-Subscription |
**Typisches Muster:** SNS → SQS → Lambda (Fan-out mit Entkopplung):
Publisher → SNS-Topic → SQS-Queue → Lambda-Funktion
→ SQS-Queue (2. Subscriber)
→ E-Mail-Subscription
==== Compute ====
^ Anforderung ^ Dienst ^ Begründung ^
| Kurzläufer (< 15 min), event-getrieben | ''std.cloud.lambda'' | Kein Server-Management; pay-per-invocation |
| Dauerhafter Server, volle Kontrolle | ''std.cloud.ec2'' | Flexible Konfiguration; persistentes Dateisystem |
| Lambda aus SQS oder DynamoDB triggern | ''std.cloud.lambda'' + ''LambdaCreateEventSourceMapping'' | Automatisches Polling |
| Batch-Verarbeitung großer Datenmengen | EC2 Spot-Instanzen | Günstig; für kurze Burst-Workloads |
==== Monitoring ====
^ Anforderung ^ Dienst ^ Begründung ^
| Metriken aus dem Programm senden | ''std.cloud.cloudwatch'' ''CWPutMetricData'' | Standard-AWS-Monitoring |
| Alarm wenn Metrik Schwellwert überschreitet | ''std.cloud.cloudwatch'' ''CWPutMetricAlarm'' | Benachrichtigung via SNS |
| Strukturierte Logs schreiben | ''std.cloud.cloudwatch'' ''CWLogsClient'' | Zentrale Log-Aggregation; filterbar via Log Insights |
----
===== AWS vs. DigitalOcean =====
^ Aufgabe ^ AWS ^ DigitalOcean ^
| Datei-Speicher | ''std.cloud.s3'' | ''std.cloud.do.spaces'' (S3-kompatibel) |
| VM starten | ''std.cloud.ec2'' | ''std.cloud.do.droplets'' |
| Managed Datenbank | (EC2 + ''std.db'') | ''std.cloud.do.databases'' (PostgreSQL/MySQL/Redis) |
| Kubernetes | (EC2 + extern) | ''std.cloud.do.kubernetes'' (DOKS) |
| Container Registry | (ECR, extern) | ''std.cloud.do.registry'' |
| Serverless | ''std.cloud.lambda'' | ''std.cloud.do.functions'' |
| App deployen (PaaS) | (Elastic Beanstalk, extern) | ''std.cloud.do.apps'' |
| DNS / Load Balancer | ''std.cloud.ec2'' (Route53 via Query-API) | ''std.cloud.do.networking'' |
| Monitoring / Alerts | ''std.cloud.cloudwatch'' | ''std.cloud.do.monitoring'' |
| Secrets / Konfiguration | ''std.cloud.secrets'' | Umgebungsvariablen in App-Spec |
| Queue / Messaging | ''std.cloud.sqs'' / ''std.cloud.sns'' | (kein nativer Queue-Dienst — SQS oder externe Lösung) |
**Wann AWS wählen:** Compliance-Anforderungen (SOC 2, HIPAA, FedRAMP), globale Verfügbarkeit in > 30 Regionen, komplexe Event-Architektur (SNS/SQS/Lambda-Ketten), IAM-Feingranularität.
**Wann DigitalOcean wählen:** Einfacheres Preismodell, schnelles Setup ohne IAM-Konfiguration, Managed Databases und Kubernetes out-of-the-box, kleinere Teams ohne AWS-Kenntnisse.
----
===== Lokal testen mit LocalStack =====
AWS-Dienste können lokal mit **LocalStack** simuliert werden — ohne echten AWS-Account und ohne Kosten.
# LocalStack starten (Docker):
docker run --rm -p 4566:4566 localstack/localstack
import std.cloud.aws.core;
import std.cloud.s3;
fn main(): int64 {
// AWSServiceConfigLocalStack() setzt endpoint auf http://localhost:4566
var cfg: AWSServiceConfig := AWSServiceConfigLocalStack();
// Dummy-Credentials (LocalStack prüft sie nicht)
var creds: AWSCreds := AWSCredentialsStatic("test"c, "test"c);
// S3ConnectConfig statt S3Connect — nutzt cfg.endpoint
var s3: S3Client := S3ConnectConfig(creds, cfg);
S3CreateBucket(s3, "test-bucket"c);
S3PutObjectFile(s3, "test-bucket"c, "hello.txt"c, "/tmp/hello.txt"c, "text/plain"c);
var data: int64 := S3GetObject(s3, "test-bucket"c, "hello.txt"c);
if (data != 0) {
Print(data as pchar); PrintLn(""c);
free(data, StrLen(data as pchar) + 1);
}
S3Close(s3);
return 0;
}
LocalStack unterstützt S3, DynamoDB, SQS, SNS, Lambda, IAM, Secrets Manager und die meisten weiteren AWS-Dienste.
----
===== Sicherheitsregeln =====
**1. Credentials nie im Code einbetten**
// Falsch:
var creds: AWSCreds := AWSCredentialsStatic("AKIA..."c, "wJalr..."c);
// Richtig:
var creds: AWSCreds := AWSCredentialsDefault(); // Env → Datei
**2. IAM Least Privilege — nur notwendige Rechte vergeben**
Für eine Anwendung die nur S3 liest: nur ''s3:GetObject'' und ''s3:ListBucket'' erlauben — kein ''s3:DeleteObject'', kein EC2-Zugriff. Über ''std.cloud.iam'' Rollen mit minimalen Policies anlegen.
**3. Secrets über Secrets Manager, nicht über Umgebungsvariablen**
Umgebungsvariablen sind im Prozess-Speicher sichtbar und landen in Logs. Secrets Manager hat Audit-Log, Rotation und Versionierung.
**4. Temporäre Credentials bevorzugen (STS AssumeRole)**
import std.cloud.iam;
var sts: STSClient := STSConnect(baseCreds);
var tempCreds: STSCredentials := STSAssumeRole(sts,
"arn:aws:iam::123456789:role/DeployRole"c,
"deploy-session"c);
// tempCreds.accessKey, .secretKey, .sessionToken
// Automatisch ablaufend nach max. 12 Stunden
**5. Response-Puffer nach sensiblen Daten sofort überschreiben**
Passwörter und Secrets nicht länger im Speicher halten als nötig:
var secret: int64 := SecretsGetString(sm, "prod/db/password"c);
// ... verwenden ...
var n: int64 := StrLen(secret as pchar);
var i: int64 := 0;
while (i < n) { poke8(secret + i, 0); i := i + 1; }
free(secret, n + 1);
**6. S3-Buckets nicht öffentlich exponieren**
Presigned URLs statt öffentlicher Bucket-Policy für zeitlich begrenzte Downloads:
// Statt: öffentlicher Bucket
// Besser: Presigned URL mit kurzer Laufzeit
var url: int64 := S3PresignURL(s3, "bucket"c, "datei.pdf"c, 900); // 15 Minuten
**7. Retry-Logik für transiente Fehler**
AWS-APIs können bei kurzer Überlastung HTTP 503 oder 429 zurückgeben. ''AWSSendWithRetry'' behandelt das automatisch:
// Direkt: AWSSendWithRetry(req, 3) — intern genutzt von allen High-Level-Funktionen
// Für eigene Low-Level-Requests: maxRetries als dritten Parameter nutzen
----
===== Entscheidungsguide =====
^ Ich will … ^ Empfehlung ^
| Datei speichern und abrufbar machen | ''std.cloud.s3'' + ''S3PresignURL'' |
| Zeitlich begrenzten Download-Link | ''std.cloud.s3'' → ''S3PresignURL(s3, bucket, key, 900)'' |
| Passwort / API-Key sicher speichern | ''std.cloud.secrets'' → ''SecretsGetString'' |
| Hintergrundaufgaben verteilen | ''std.cloud.sqs'' (Worker) |
| Push-Benachrichtigung an viele Systeme | ''std.cloud.sns'' |
| Code event-getrieben ausführen | ''std.cloud.lambda'' + ''LambdaInvoke'' |
| VM starten (AWS) | ''std.cloud.ec2'' + ''EC2RunInstances'' / ''EC2DescribeInstanceById'' |
| VM starten (DigitalOcean) | ''std.cloud.do.droplets'' + ''DropletCreateSimple'' / ''DropletWait'' |
| Managed PostgreSQL / MySQL (DO) | ''std.cloud.do.databases'' + ''DatabaseCreate'' |
| Kubernetes-Cluster anlegen (DO) | ''std.cloud.do.kubernetes'' + ''K8sClusterCreate'' / ''K8sKubeconfig'' |
| DNS-Eintrag setzen (DO) | ''std.cloud.do.networking'' + ''DomainRecordCreate'' |
| Container-Image speichern (DO) | ''std.cloud.do.registry'' + ''RegistryCreate'' |
| Metriken überwachen (AWS) | ''std.cloud.cloudwatch'' + ''CWPutMetricData'' / ''CWPutMetricAlarm'' |
| Lokal testen ohne AWS-Account | ''AWSServiceConfigLocalStack()'' + LocalStack via Docker |
| Temporäre Credentials für Deployment | ''std.cloud.iam'' + ''STSAssumeRole'' |
| DNS-Record anlegen / aktualisieren (CF) | ''std.cloud.cf.dns'' + ''DNSRecordUpsert'' |
| Cloudflare Zone pausieren / reaktivieren | ''std.cloud.cf.zones'' + ''ZonePause'' / ''ZoneUnpause'' |
| Worker-Script deployen | ''std.cloud.cf.workers'' + ''WorkerDeploy'' |
| Worker-Route anlegen (URL-Muster → Worker) | ''std.cloud.cf.workers'' + ''WorkerRouteCreate'' |
| Worker-Secret (Env-Variable) setzen | ''std.cloud.cf.workers'' + ''WorkerSecretSet'' |
| KV-Wert lesen / schreiben (mit TTL) | ''std.cloud.cf.kv'' + ''KVGet'' / ''KVPutWithTTL'' |
| R2-Objekt hochladen / herunterladen | ''std.cloud.cf.r2'' + ''R2Upload'' / ''R2Download'' |
| R2 Presigned URL erzeugen | ''std.cloud.cf.r2'' + ''R2PresignedURL'' |
| D1-Datenbank abfragen | ''std.cloud.cf.d1'' + ''D1Query'' |
| Cache nach Deployment leeren | ''std.cloud.cf.cache'' + ''CachePurgeAll'' |
| IP-Adresse sperren (WAF) | ''std.cloud.cf.waf'' + ''FirewallBlockIP'' |
| Rate-Limiting für Endpunkt | ''std.cloud.cf.waf'' + ''RateLimitCreate'' |
| Cloudflare Pages deployen | ''std.cloud.cf.pages'' + ''PagesDeployDir'' |
| Tunnel ohne offenen Port | ''std.cloud.cf.tunnel'' + ''TunnelCreate'' / ''TunnelToken'' |
| E-Mail-Weiterleitung an Worker | ''std.cloud.cf.email'' + ''EmailRuleCreateWorker'' |
| Zone-Analytics abfragen | ''std.cloud.cf.analytics'' + ''AnalyticsDashboard'' |
| Datei in Google Cloud Storage speichern | ''std.cloud.gcp.storage'' + ''GCSUpload'' |
| GCS-Datei herunterladen | ''std.cloud.gcp.storage'' + ''GCSDownload'' |
| GCP-Secret auslesen / anlegen | ''std.cloud.gcp.secrets'' + ''SecretGet'' / ''SecretCreate'' |
| Firestore-Dokument schreiben / lesen | ''std.cloud.gcp.firestore'' + ''FSCreate'' / ''FSGet'' |
| GCE-VM anlegen und warten | ''std.cloud.gcp.compute'' + ''GCEInstanceCreate'' + ''GCEOperationWait'' |
| Pub/Sub-Nachricht publishen | ''std.cloud.gcp.pubsub'' + ''PubSubPublish'' |
| Cloud Function aufrufen | ''std.cloud.gcp.functions'' + ''GCFInvoke'' |
| Log-Eintrag in Cloud Logging schreiben | ''std.cloud.gcp.logging'' + ''LogWrite'' |
| Custom Metric in Cloud Monitoring schreiben | ''std.cloud.gcp.logging'' + ''MonWriteMetric'' |
| GCP Credentials laden (Service Account / ADC) | ''std.cloud.gcp.credentials'' + ''GCPCredentialsDefault'' / ''GCPCredentialsFromFile'' |
Letzte Aktualisierung: 2026-06-13