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.

std.cloud (Unit-Referenz) · AWS-Units · DigitalOcean-Units · Cloudflare-Units · GCP-Units · 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<int64> := [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® 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.s3S3PresignURL(s3, bucket, key, 900)
Passwort / API-Key sicher speichern std.cloud.secretsSecretsGetString
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