====== 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