From 8883093024456ccef2ad182d36d79b6fe90e2a7e Mon Sep 17 00:00:00 2001 From: "fastedge-plugin-sync[bot]" Date: Fri, 2 Oct 2026 09:07:59 +0000 Subject: [PATCH] =?UTF-8?q?release:=20v0.2.12=20=E2=80=94=20update=20docs?= =?UTF-8?q?=20index=20and=20bump=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude-plugin/marketplace.json | 2 +- .../.codex-plugin/plugin.json | 2 +- plugins/gcore-fastedge-codex/docs-index.json | 1428 +++++++++++---- .../reference/cdn/examples-ab-testing-as.md | 236 ++- .../reference/cdn/examples-api-key-as.md | 171 +- .../reference/cdn/examples-body-as.md | 219 ++- .../cdn/examples-custom-error-pages-as.md | 242 ++- .../reference/cdn/examples-env-secrets-as.md | 143 +- .../reference/cdn/examples-headers-as.md | 22 +- .../reference/cdn/examples-http-call-as.md | 10 +- .../cdn/examples-large-dictionary-as.md | 138 +- .../scaffold/reference/cdn/ab-testing-as.md | 236 +-- .../scaffold/reference/cdn/auth-jwt-as.md | 12 +- .../skills/scaffold/reference/cdn/base-as.md | 125 +- .../skills/scaffold/reference/cdn/body-as.md | 223 +-- .../reference/cdn/custom-error-pages-as.md | 244 ++- .../scaffold/reference/cdn/env-secrets-as.md | 143 +- .../scaffold/reference/cdn/geo-redirect-as.md | 188 +- .../reference/cdn/large-dictionary-as.md | 132 +- .../scaffold/reference/cdn/properties-as.md | 273 ++- .../.cursor-plugin/plugin.json | 2 +- plugins/gcore-fastedge-cursor/docs-index.json | 1428 +++++++++++---- .../reference/cdn/examples-ab-testing-as.md | 236 ++- .../reference/cdn/examples-api-key-as.md | 171 +- .../reference/cdn/examples-body-as.md | 219 ++- .../cdn/examples-custom-error-pages-as.md | 242 ++- .../reference/cdn/examples-env-secrets-as.md | 143 +- .../reference/cdn/examples-headers-as.md | 22 +- .../reference/cdn/examples-http-call-as.md | 10 +- .../cdn/examples-large-dictionary-as.md | 138 +- .../scaffold/reference/cdn/ab-testing-as.md | 236 +-- .../scaffold/reference/cdn/auth-jwt-as.md | 12 +- .../skills/scaffold/reference/cdn/base-as.md | 125 +- .../skills/scaffold/reference/cdn/body-as.md | 223 +-- .../reference/cdn/custom-error-pages-as.md | 244 ++- .../scaffold/reference/cdn/env-secrets-as.md | 143 +- .../scaffold/reference/cdn/geo-redirect-as.md | 188 +- .../reference/cdn/large-dictionary-as.md | 132 +- .../scaffold/reference/cdn/properties-as.md | 273 ++- .../gcore-fastedge/.claude-plugin/plugin.json | 2 +- plugins/gcore-fastedge/docs-index.json | 1584 ++++++++++++----- 41 files changed, 7064 insertions(+), 2898 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 20b0281..10d3ef5 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -13,7 +13,7 @@ "name": "gcore-fastedge", "source": "./plugins/gcore-fastedge", "description": "Build, deploy, and manage serverless edge applications on Gcore FastEdge", - "version": "0.2.11" + "version": "0.2.12" } ] } diff --git a/plugins/gcore-fastedge-codex/.codex-plugin/plugin.json b/plugins/gcore-fastedge-codex/.codex-plugin/plugin.json index 7329354..b704e39 100644 --- a/plugins/gcore-fastedge-codex/.codex-plugin/plugin.json +++ b/plugins/gcore-fastedge-codex/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "gcore-fastedge", - "version": "0.2.11", + "version": "0.2.12", "description": "Codex plugin for Gcore FastEdge with local indexed docs and MCP-backed build/deploy workflows.", "author": { "name": "Gcore", diff --git a/plugins/gcore-fastedge-codex/docs-index.json b/plugins/gcore-fastedge-codex/docs-index.json index c340e5f..98d6811 100644 --- a/plugins/gcore-fastedge-codex/docs-index.json +++ b/plugins/gcore-fastedge-codex/docs-index.json @@ -1,9 +1,9 @@ { "schema_version": "1.0.0", - "generated_at": "2026-09-24T09:24:50.728Z", + "generated_at": "2026-10-02T09:07:59.528Z", "source": { "repo": "fastedge-plugin", - "commit": "503a8fc", + "commit": "0db9306", "pipeline": "sync-reference-docs" }, "topics": [ @@ -528,7 +528,7 @@ { "id": "cdn-examples-ab-testing-as", "title": "A/B Testing — AssemblyScript (CDN)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 updated: 2026-08-17 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-ab-testing-as.md", "skill": "fastedge-docs", "category": "examples", @@ -548,11 +548,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "8e3bb621bc013a0aed7e52122066b417ad62a207", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-08-17", + "2026-09-29", "testing", "cookie-based", "traffic", @@ -562,7 +562,7 @@ "requests", "different" ], - "content_sha256": "9546c33b6d3c8657538a75f442e7738541cdad753fc7b88a1cd759cc036629f0", + "content_sha256": "5fa3790d1f7f070a89f606e6999200868933dc65e36fe612d9a0095c6fe0ca1a", "sections": [ { "id": "cdn-examples-ab-testing-as#introduction", @@ -577,11 +577,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "8e3bb621bc013a0aed7e52122066b417ad62a207", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-08-17", + "2026-09-29", "testing", "assemblyscript", "cdn", @@ -990,6 +990,186 @@ "assignment", "logic" ] + }, + { + "id": "cdn-examples-ab-testing-as#source-material", + "heading": "Source Material", + "level": 2, + "anchor": "source-material", + "line_start": 228, + "line_end": 413, + "keywords": [ + "source", + "material", + "file", + "examples", + "abtesting", + "assembly", + "index.ts", + "export", + "gcoredev", + "proxy-wasm-sdk-as", + "proxy", + "import", + "context", + "filterheadersstatusvalues", + "get_property", + "log", + "loglevelvalues", + "registerrootcontext", + "rootcontext", + "send_http_response", + "set_property", + "stream_context", + "getenv", + "setloglevel", + "fastedge", + "getcurrenttime", + "pro" + ] + }, + { + "id": "cdn-examples-ab-testing-as#what-it-does", + "heading": "What it does", + "level": 2, + "anchor": "what-it-does", + "line_start": 414, + "line_end": 426, + "keywords": [ + "onrequestheaders", + "app", + "checks", + "existing", + "experiment", + "cookie", + "fe_exp_", + "experiment_name", + "found", + "assigns", + "user", + "variant", + "split", + "rewrites", + "request", + "path", + "prepending", + "variant-specific", + "prefix", + "e.g.", + "variant-a", + "original", + "adds", + "x-experiment", + "x-variant", + "headers", + "upstream", + "visibility.", + "onresponseheaders", + "sets", + "set-cookie", + "header", + "persist", + "assignment", + "sub" + ] + }, + { + "id": "cdn-examples-ab-testing-as#configuration", + "heading": "Configuration", + "level": 2, + "anchor": "configuration", + "line_start": 427, + "line_end": 438, + "keywords": [ + "configuration", + "set", + "following", + "environment", + "variables", + "your", + "fastedge", + "application", + "variable", + "example", + "description", + "----------", + "---------", + "-------------", + "experiment_name", + "homepage-redesign", + "name", + "experiment", + "required", + "variant_a_path", + "variant-a", + "path", + "prefix", + "variant", + "variant_b_path", + "variant-b", + "origin", + "server", + "serve", + "different", + "content", + "prefix." + ] + }, + { + "id": "cdn-examples-ab-testing-as#build", + "heading": "Build", + "level": 2, + "anchor": "build", + "line_start": 439, + "line_end": 452, + "keywords": [ + "build", + "pnpm", + "install", + "run", + "asbuild", + "output", + "file", + "description", + "------", + "-------------", + "abtesting.wasm", + "optimised", + "release", + "binary", + "upload", + "fastedge", + "abtesting-debug.wasm", + "debug", + "source", + "maps" + ] + }, + { + "id": "cdn-examples-ab-testing-as#deploy", + "heading": "Deploy", + "level": 2, + "anchor": "deploy", + "line_start": 453, + "line_end": 457, + "keywords": [ + "deploy", + "upload", + "build", + "abtesting.wasm", + "fastedge", + "portal", + "attach", + "your", + "cdn", + "application.", + "configure", + "experiment", + "environment", + "variables", + "application", + "settings." + ] } ] }, @@ -1652,7 +1832,7 @@ { "id": "cdn-examples-api-key-as", "title": "CDN Example: API Key Authentication (AssemblyScript)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 updated: 2026-08-20 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-api-key-as.md", "skill": "fastedge-docs", "category": "examples", @@ -1672,11 +1852,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "8e3bb621bc013a0aed7e52122066b417ad62a207", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-08-20", + "2026-09-29", "---", "type", "example", @@ -1686,7 +1866,7 @@ "api-key-auth", "header-validation" ], - "content_sha256": "d47decc23a11899c2cdd3360355fb8c5e9db6fb48d6b2ca5c90bee4cc5c8ae2b", + "content_sha256": "af7f651d7d617a498e1776ce8c5ccf0e50fb0d53cb7431ecd40ddbbdea8aa5bf", "sections": [ { "id": "cdn-examples-api-key-as#introduction", @@ -1701,11 +1881,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "8e3bb621bc013a0aed7e52122066b417ad62a207", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-08-20", + "2026-09-29", "---", "type", "example", @@ -2060,89 +2240,259 @@ "deployment", "guide" ] - } - ] - }, - { - "id": "cdn-examples-api-key-rust", - "title": "API Key Validation — CDN (Rust)", - "description": "auto-updated: true sources: - id: fastedge-sdk-rust ref: main commit: 6eedcca9d5c0ddd4ff79ca475965393891da2d75 updated: 2026-09-22 -->", - "path": "plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-api-key-rust.md", - "skill": "fastedge-docs", - "category": "examples", - "app_types": [ - "cdn" - ], - "languages": [ - "rust" - ], - "tags": [ - "examples", - "fastedge-docs", - "cdn", - "rust", - "auto-updated", - "true", - "sources", - "fastedge-sdk-rust", - "ref", - "main", - "commit", - "6eedcca9d5c0ddd4ff79ca475965393891da2d75", - "updated", - "2026-09-22", - "api", - "key", - "validation", - "overview", - "validates", - "incoming", - "requests", - "against" - ], - "content_sha256": "6c28a1664d639ebeb12397dea7294bdd293848795b65c88c2f5aab81e2bee614", - "sections": [ - { - "id": "cdn-examples-api-key-rust#introduction", - "heading": "Introduction", - "level": 1, - "anchor": "introduction", - "line_start": 1, - "line_end": 11, - "keywords": [ - "auto-updated", - "true", - "sources", - "fastedge-sdk-rust", - "ref", - "main", - "commit", - "6eedcca9d5c0ddd4ff79ca475965393891da2d75", - "updated", - "2026-09-22", - "api", - "key", - "validation", - "cdn", - "rust" - ] }, { - "id": "cdn-examples-api-key-rust#overview", - "heading": "Overview", + "id": "cdn-examples-api-key-as#source-material", + "heading": "Source Material", "level": 2, - "anchor": "overview", - "line_start": 12, - "line_end": 15, + "anchor": "source-material", + "line_start": 203, + "line_end": 326, "keywords": [ - "overview", - "validates", - "incoming", - "requests", - "against", - "stored", - "secret", - "using", + "source", + "material", + "file", + "examples", + "apikey", + "assembly", + "index.ts", + "export", + "gcoredev", + "proxy-wasm-sdk-as", + "proxy", + "import", + "context", + "filterheadersstatusvalues", + "headerpair", + "log", + "loglevelvalues", + "makeheaderpair", + "registerrootcontext", + "rootcontext", + "send_http_response", + "stream_context", + "getsecret", + "setloglevel", + "fastedge", + "const", + "unauthorized", + "u32", + "401", + "forbidd" + ] + }, + { + "id": "cdn-examples-api-key-as#what-it-does", + "heading": "What it does", + "level": 2, + "anchor": "what-it-does", + "line_start": 327, + "line_end": 340, + "keywords": [ + "onrequestheaders", + "app", + "reads", + "expected", + "api", + "key", + "api_key", + "secret.", + "checks", + "x-api-key", + "request", + "header.", + "returns", + "401", + "unauthorized", + "header", + "missing.", + "403", + "forbidden", + "match.", + "success", + "clears", + "forwarding", + "upstream", + "origin", + "proxy-wasm", + ".remove", + "sets", + "value", + "empty", + "string", + "rather", + "deleting", + "simpler", + "alternative", + "jwt", + "validation", + "you" + ] + }, + { + "id": "cdn-examples-api-key-as#configuration", + "heading": "Configuration", + "level": 2, + "anchor": "configuration", + "line_start": 341, + "line_end": 348, + "keywords": [ + "configuration", + "set", + "following", + "your", + "fastedge", + "application", + "name", + "type", + "description", + "------", + "-------------", + "api_key", + "secret", + "expected", + "api", + "key", + "value" + ] + }, + { + "id": "cdn-examples-api-key-as#build", + "heading": "Build", + "level": 2, + "anchor": "build", + "line_start": 349, + "line_end": 362, + "keywords": [ + "build", + "pnpm", + "install", + "run", + "asbuild", + "output", + "file", + "description", + "------", + "-------------", + "apikey.wasm", + "optimised", + "release", + "binary", + "upload", + "fastedge", + "apikey-debug.wasm", + "debug", + "source", + "maps" + ] + }, + { + "id": "cdn-examples-api-key-as#deploy", + "heading": "Deploy", + "level": 2, + "anchor": "deploy", + "line_start": 363, + "line_end": 367, + "keywords": [ + "deploy", + "upload", + "build", + "apikey.wasm", + "fastedge", + "portal", + "attach", + "your", + "cdn", + "application.", + "configure", + "api_key", + "secret", + "application", + "settings." + ] + } + ] + }, + { + "id": "cdn-examples-api-key-rust", + "title": "API Key Validation — CDN (Rust)", + "description": "auto-updated: true sources: - id: fastedge-sdk-rust ref: main commit: 6eedcca9d5c0ddd4ff79ca475965393891da2d75 updated: 2026-09-22 -->", + "path": "plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-api-key-rust.md", + "skill": "fastedge-docs", + "category": "examples", + "app_types": [ + "cdn" + ], + "languages": [ + "rust" + ], + "tags": [ + "examples", + "fastedge-docs", + "cdn", + "rust", + "auto-updated", + "true", + "sources", + "fastedge-sdk-rust", + "ref", + "main", + "commit", + "6eedcca9d5c0ddd4ff79ca475965393891da2d75", + "updated", + "2026-09-22", + "api", + "key", + "validation", + "overview", + "validates", + "incoming", + "requests", + "against" + ], + "content_sha256": "6c28a1664d639ebeb12397dea7294bdd293848795b65c88c2f5aab81e2bee614", + "sections": [ + { + "id": "cdn-examples-api-key-rust#introduction", + "heading": "Introduction", + "level": 1, + "anchor": "introduction", + "line_start": 1, + "line_end": 11, + "keywords": [ + "auto-updated", + "true", + "sources", + "fastedge-sdk-rust", + "ref", + "main", + "commit", + "6eedcca9d5c0ddd4ff79ca475965393891da2d75", + "updated", + "2026-09-22", + "api", + "key", + "validation", + "cdn", + "rust" + ] + }, + { + "id": "cdn-examples-api-key-rust#overview", + "heading": "Overview", + "level": 2, + "anchor": "overview", + "line_start": 12, + "line_end": 15, + "keywords": [ + "overview", + "validates", + "incoming", + "requests", + "against", + "stored", + "secret", + "using", "x-api-key", "request", "header.", @@ -3428,7 +3778,7 @@ { "id": "cdn-examples-body-as", "title": "Body Manipulation — AssemblyScript (CDN)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 updated: 2026-08-20 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-body-as.md", "skill": "fastedge-docs", "category": "examples", @@ -3448,11 +3798,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "8e3bb621bc013a0aed7e52122066b417ad62a207", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-08-20", + "2026-09-29", "---", "title", "body", @@ -3462,7 +3812,7 @@ "app_type", "languages" ], - "content_sha256": "94226f2f7783dd5978e697ebea97008427e93dbddfd4b033a8c05d74268e9a34", + "content_sha256": "418f834e01924510268a0c8bb73e37ee660382ae3f31079a506b50e37b508188", "sections": [ { "id": "cdn-examples-body-as#introduction", @@ -3477,11 +3827,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "8e3bb621bc013a0aed7e52122066b417ad62a207", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-08-20", + "2026-09-29", "---", "title", "body", @@ -3908,25 +4258,158 @@ "body", "buffering" ] - } - ] - }, - { - "id": "cdn-examples-body-rust", - "title": "CDN Body Inspection and Modification — Rust", - "description": "auto-updated: true sources: - id: fastedge-sdk-rust ref: main commit: 6347a7c2fda0d03e66f1214db5eec041c16801b7 updated: 2026-07-23 -->", - "path": "plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-body-rust.md", - "skill": "fastedge-docs", - "category": "examples", - "app_types": [ - "cdn" - ], - "languages": [ - "rust" - ], - "tags": [ - "examples", - "fastedge-docs", + }, + { + "id": "cdn-examples-body-as#source-material", + "heading": "Source Material", + "level": 2, + "anchor": "source-material", + "line_start": 341, + "line_end": 519, + "keywords": [ + "source", + "material", + "file", + "examples", + "body", + "assembly", + "index.ts", + "export", + "gcoredev", + "proxy-wasm-sdk-as", + "proxy", + "exports", + "required", + "functions", + "interact", + "us.", + "import", + "buffertypevalues", + "context", + "filterdatastatusvalues", + "filterheadersstatusvalues", + "get_buffer_bytes", + "get_property", + "log", + "loglevelvalues", + "registerrootcontext", + "rootcontext", + "set_buffer_bytes", + "set_property", + "stream_context" + ] + }, + { + "id": "cdn-examples-body-as#what-it-does", + "heading": "What it does", + "level": 2, + "anchor": "what-it-does", + "line_start": 520, + "line_end": 534, + "keywords": [ + "onrequestheaders", + "removes", + "content-length", + "header", + "required", + "because", + "body", + "content", + "altered.", + "onrequestbody", + "buffers", + "full", + "request", + "checks", + "whether", + "contains", + "word", + "client", + "found", + "replaced", + "redaction", + "notice.", + "onresponseheaders", + "sets", + "transfer-encoding", + "chunked", + "captures", + "content-type", + "runtime", + "property.", + "onresponsebody", + "logs", + "url" + ] + }, + { + "id": "cdn-examples-body-as#build", + "heading": "Build", + "level": 2, + "anchor": "build", + "line_start": 535, + "line_end": 548, + "keywords": [ + "build", + "pnpm", + "install", + "run", + "asbuild", + "output", + "file", + "description", + "-----------------------", + "--------------------------------------------------", + "body.wasm", + "optimised", + "release", + "binary", + "upload", + "fastedge", + "body-debug.wasm", + "debug", + "source", + "maps" + ] + }, + { + "id": "cdn-examples-body-as#deploy", + "heading": "Deploy", + "level": 2, + "anchor": "deploy", + "line_start": 549, + "line_end": 553, + "keywords": [ + "deploy", + "upload", + "build", + "body.wasm", + "fastedge", + "portal", + "attach", + "your", + "cdn", + "application." + ] + } + ] + }, + { + "id": "cdn-examples-body-rust", + "title": "CDN Body Inspection and Modification — Rust", + "description": "auto-updated: true sources: - id: fastedge-sdk-rust ref: main commit: 6347a7c2fda0d03e66f1214db5eec041c16801b7 updated: 2026-07-23 -->", + "path": "plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-body-rust.md", + "skill": "fastedge-docs", + "category": "examples", + "app_types": [ + "cdn" + ], + "languages": [ + "rust" + ], + "tags": [ + "examples", + "fastedge-docs", "cdn", "rust", "auto-updated", @@ -7692,7 +8175,7 @@ { "id": "cdn-examples-custom-error-pages-as", "title": "Custom Error Pages — AssemblyScript (CDN)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-custom-error-pages-as.md", "skill": "fastedge-docs", "category": "examples", @@ -7712,11 +8195,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -7726,7 +8209,7 @@ "response-headers", "response-body" ], - "content_sha256": "db4273297015e84837b705d18e071b4684410000b7bc9fcf363fe82c1c58ea15", + "content_sha256": "4db5f9268da49b745f237677f4cba2a096cdd53f63cdad4dc043a01f343f9f7d", "sections": [ { "id": "cdn-examples-custom-error-pages-as#introduction", @@ -7741,11 +8224,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -8175,6 +8658,147 @@ "hook", "sequencing" ] + }, + { + "id": "cdn-examples-custom-error-pages-as#source-material", + "heading": "Source Material", + "level": 2, + "anchor": "source-material", + "line_start": 296, + "line_end": 499, + "keywords": [ + "source", + "material", + "file", + "examples", + "customerrorpages", + "assembly", + "index.ts", + "export", + "gcoredev", + "proxy-wasm-sdk-as", + "proxy", + "import", + "context", + "filterdatastatusvalues", + "filterheadersstatusvalues", + "get_property", + "log", + "loglevelvalues", + "registerrootcontext", + "rootcontext", + "set_buffer_bytes", + "buffertypevalues", + "stream_context", + "setloglevel", + "fastedge", + "class", + "errorpagesroot" + ] + }, + { + "id": "cdn-examples-custom-error-pages-as#what-it-does", + "heading": "What it does", + "level": 2, + "anchor": "what-it-does", + "line_start": 500, + "line_end": 512, + "keywords": [ + "onresponseheaders", + "app", + "reads", + "response.status", + "property.", + "400-599", + "range", + "sets", + "content-type", + "text", + "html", + "prepares", + "body", + "replacement.", + "onresponsebody", + "buffers", + "full", + "response", + "replaces", + "styled", + "page", + "containing", + "numeric", + "status", + "code", + "human-readable", + "error", + "title", + "e.g.", + "found", + "bad", + "gateway", + "description", + "explaining", + "went", + "wrong", + "category", + "label", + "client", + "server" + ] + }, + { + "id": "cdn-examples-custom-error-pages-as#build", + "heading": "Build", + "level": 2, + "anchor": "build", + "line_start": 513, + "line_end": 526, + "keywords": [ + "build", + "pnpm", + "install", + "run", + "asbuild", + "output", + "file", + "description", + "------", + "-------------", + "customerrorpages.wasm", + "optimised", + "release", + "binary", + "upload", + "fastedge", + "customerrorpages-debug.wasm", + "debug", + "source", + "maps" + ] + }, + { + "id": "cdn-examples-custom-error-pages-as#deploy", + "heading": "Deploy", + "level": 2, + "anchor": "deploy", + "line_start": 527, + "line_end": 531, + "keywords": [ + "deploy", + "upload", + "build", + "customerrorpages.wasm", + "fastedge", + "portal", + "attach", + "your", + "cdn", + "application.", + "environment", + "variables", + "secrets", + "required." + ] } ] }, @@ -8733,7 +9357,7 @@ { "id": "cdn-examples-env-secrets-as", "title": "Environment Variables and Secrets — CDN (AssemblyScript)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 updated: 2026-08-20 --> --- type: example app_type: cdn languages: [as] capabilities: [env-vars, secrets] ---", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 --> --- type: example app_type: cdn languages: [as] capabilities: [env-vars, secrets] ---", "path": "plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-env-secrets-as.md", "skill": "fastedge-docs", "category": "examples", @@ -8753,11 +9377,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "8e3bb621bc013a0aed7e52122066b417ad62a207", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-08-20", + "2026-09-29", "---", "type", "example", @@ -8768,7 +9392,7 @@ "secrets", "environment" ], - "content_sha256": "0c73ed18f10ce8d8e06a88f54b4d3f23ac0b67968d5c25e974066c47408cfacb", + "content_sha256": "97297fd0847bec88008f332984d685818a0c9bbd39ceb6e7fcfdd36a8310f413", "sections": [ { "id": "cdn-examples-env-secrets-as#introduction", @@ -8783,11 +9407,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "8e3bb621bc013a0aed7e52122066b417ad62a207", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-08-20", + "2026-09-29", "---", "type", "example", @@ -9189,14 +9813,229 @@ "patterns", "platform-overview", "secret", - "management", - "deployment", - "configuration", - "best-practices", - "logging", - "levels", - "handling", - "guidelines" + "management", + "deployment", + "configuration", + "best-practices", + "logging", + "levels", + "handling", + "guidelines" + ] + }, + { + "id": "cdn-examples-env-secrets-as#source-material", + "heading": "Source Material", + "level": 2, + "anchor": "source-material", + "line_start": 229, + "line_end": 313, + "keywords": [ + "source", + "material", + "file", + "examples", + "variablesandsecrets", + "assembly", + "index.ts", + "export", + "gcoredev", + "proxy-wasm-sdk-as", + "proxy", + "exports", + "required", + "functions", + "interact", + "us.", + "import", + "context", + "filterheadersstatusvalues", + "log", + "loglevelvalues", + "registerrootcontext", + "rootcontext", + "stream_context", + "getenv", + "getsecret", + "setloglevel", + "fastedge" + ] + }, + { + "id": "cdn-examples-env-secrets-as#what-it-does", + "heading": "What it does", + "level": 2, + "anchor": "what-it-does", + "line_start": 314, + "line_end": 326, + "keywords": [ + "onrequestheaders", + "app", + "reads", + "username", + "environment", + "variable", + "using", + "getenv", + "password", + "secret", + "getsecret", + "logs", + "values", + "retrieved", + "without", + "logging", + "value", + "itself", + "injects", + "them", + "x-env-username", + "x-env-password", + "request", + "headers", + "upstream", + "receives", + "them.", + "useful", + "reference", + "understanding", + "access", + "variables", + "secrets", + "within", + "fastedge", + "plugin.", + "security", + "warning", + "neve" + ] + }, + { + "id": "cdn-examples-env-secrets-as#configuration", + "heading": "Configuration", + "level": 2, + "anchor": "configuration", + "line_start": 327, + "line_end": 335, + "keywords": [ + "configuration", + "set", + "following", + "your", + "fastedge", + "application", + "name", + "type", + "description", + "----------", + "--------------------", + "--------------------------------------", + "username", + "environment", + "variable", + "value", + "forward", + "upstream", + "password", + "secret" + ] + }, + { + "id": "cdn-examples-env-secrets-as#local-testing", + "heading": "Local testing", + "level": 2, + "anchor": "local-testing", + "line_start": 336, + "line_end": 346, + "keywords": [ + "local", + "testing", + "fixture", + "fixtures", + "happy-path.test.json", + "uses", + "dotenv", + "enabled", + "true", + "load", + "values", + ".env", + "runner", + "maps", + "fastedge_var_env_", + "name", + "getenv", + "fastedge_var_secret_", + "getsecret", + "test", + "locally", + "visual", + "debugger", + "create", + "fastedge_var_env_username", + "my-username", + "fastedge_var_secret_password", + "my-password" + ] + }, + { + "id": "cdn-examples-env-secrets-as#build", + "heading": "Build", + "level": 2, + "anchor": "build", + "line_start": 347, + "line_end": 360, + "keywords": [ + "build", + "pnpm", + "install", + "run", + "asbuild", + "output", + "file", + "description", + "--------------------------------------", + "--------------------------------------------------", + "variablesandsecrets.wasm", + "optimised", + "release", + "binary", + "upload", + "fastedge", + "variablesandsecrets-debug.wasm", + "debug", + "source", + "maps" + ] + }, + { + "id": "cdn-examples-env-secrets-as#deploy", + "heading": "Deploy", + "level": 2, + "anchor": "deploy", + "line_start": 361, + "line_end": 365, + "keywords": [ + "deploy", + "upload", + "build", + "variablesandsecrets.wasm", + "fastedge", + "portal", + "https", + "portal.gcore.com", + "attach", + "your", + "cdn", + "application.", + "configure", + "username", + "environment", + "variable", + "password", + "secret", + "application", + "settings." ] } ] @@ -11900,7 +12739,7 @@ { "id": "cdn-examples-headers-as", "title": "CDN Headers Manipulation — AssemblyScript", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-headers-as.md", "skill": "fastedge-docs", "category": "examples", @@ -11922,11 +12761,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -11936,7 +12775,7 @@ "headers", "request-headers" ], - "content_sha256": "eb268cb0cde0417bb082dbadb140da4f26f837195f2c6d93a907bc1643fc243e", + "content_sha256": "f4799cc1d18e1e473a374b576a6be37324a4d3f5957d0957a136e10508249d0c", "sections": [ { "id": "cdn-examples-headers-as#introduction", @@ -11951,11 +12790,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -12093,11 +12932,11 @@ "method", "signature", "description", - "--------------------------", + "----------------------", "---------------------------------------", - "-----------------------------------------------------------------------------------------------------", + "---------------------------------------------------------------------------------------------", "get", - "nam" + "name" ] }, { @@ -12934,7 +13773,7 @@ { "id": "cdn-examples-http-call-as", "title": "cdn-examples-http-call-as", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-http-call-as.md", "skill": "fastedge-docs", "category": "examples", @@ -12956,11 +13795,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "overview", "httpcall", "dispatches", @@ -12971,7 +13810,7 @@ "fastedge", "proxy-wasm" ], - "content_sha256": "5fd3877e09d4896bd6c69ed4c0cb9cc56dc6cd0d8fadb01c02391285f4b8bce6", + "content_sha256": "534a12e5822a9394bae1f5757847a68ad870ab3f5eb02922c6f2b2cb3235efd9", "sections": [ { "id": "cdn-examples-http-call-as#introduction", @@ -12986,11 +13825,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22" + "2026-09-29" ] }, { @@ -13039,7 +13878,7 @@ "level": 2, "anchor": "api-patterns", "line_start": 14, - "line_end": 101, + "line_end": 103, "keywords": [ "api", "patterns", @@ -13088,8 +13927,8 @@ "heading": "Common Patterns", "level": 2, "anchor": "common-patterns", - "line_start": 102, - "line_end": 286, + "line_start": 104, + "line_end": 288, "keywords": [ "common", "patterns", @@ -13126,8 +13965,8 @@ "heading": "Build", "level": 2, "anchor": "build", - "line_start": 287, - "line_end": 306, + "line_start": 289, + "line_end": 308, "keywords": [ "build", "pnpm", @@ -13173,8 +14012,8 @@ "heading": "Gotchas", "level": 2, "anchor": "gotchas", - "line_start": 307, - "line_end": 318, + "line_start": 309, + "line_end": 320, "keywords": [ "gotchas", "closures", @@ -13218,8 +14057,8 @@ "heading": "Related", "level": 2, "anchor": "related", - "line_start": 319, - "line_end": 324, + "line_start": 321, + "line_end": 326, "keywords": [ "related", "sdk", @@ -15054,7 +15893,7 @@ { "id": "cdn-examples-large-dictionary-as", "title": "Large Dictionary — AssemblyScript (CDN)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-large-dictionary-as.md", "skill": "fastedge-docs", "category": "examples", @@ -15074,11 +15913,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "large", "dictionary", "overview", @@ -15087,7 +15926,7 @@ "environment", "variables" ], - "content_sha256": "06c3e8963a2738ac48fbbfbf6217cded6950009199039727a0ed53963ab8474d", + "content_sha256": "669d8a1223ebb90b395c33eccbec9eed8b6e24c3d6de414d70dfa49bf3cadf0f", "sections": [ { "id": "cdn-examples-large-dictionary-as#introduction", @@ -15102,11 +15941,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "large", "dictionary", "assemblyscript", @@ -15199,7 +16038,7 @@ "level": 2, "anchor": "pattern-read-in-onrequestheaders", "line_start": 45, - "line_end": 88, + "line_end": 92, "keywords": [ "pattern", "read", @@ -15237,8 +16076,8 @@ "heading": "Environment Variables", "level": 2, "anchor": "environment-variables", - "line_start": 89, - "line_end": 94, + "line_start": 93, + "line_end": 98, "keywords": [ "environment", "variables", @@ -15267,8 +16106,8 @@ "heading": "Build", "level": 2, "anchor": "build", - "line_start": 95, - "line_end": 121, + "line_start": 99, + "line_end": 125, "keywords": [ "build", "package", @@ -15306,8 +16145,8 @@ "heading": "Deployment", "level": 2, "anchor": "deployment", - "line_start": 122, - "line_end": 125, + "line_start": 126, + "line_end": 129, "keywords": [ "deployment", "upload", @@ -15332,8 +16171,8 @@ "heading": "Gotchas", "level": 2, "anchor": "gotchas", - "line_start": 126, - "line_end": 133, + "line_start": 130, + "line_end": 137, "keywords": [ "gotchas", "empty", @@ -15382,8 +16221,8 @@ "heading": "See Also", "level": 2, "anchor": "see-also", - "line_start": 134, - "line_end": 139, + "line_start": 138, + "line_end": 143, "keywords": [ "see", "getenv", @@ -15410,205 +16249,6 @@ "patterns", "fastedge-docs" ] - }, - { - "id": "cdn-examples-large-dictionary-as#source-material", - "heading": "Source Material", - "level": 2, - "anchor": "source-material", - "line_start": 140, - "line_end": 223, - "keywords": [ - "source", - "material", - "file", - "examples", - "largedictionary", - "assembly", - "index.ts", - "export", - "gcoredev", - "proxy-wasm-sdk-as", - "proxy", - "import", - "context", - "filterheadersstatusvalues", - "log", - "loglevelvalues", - "registerrootcontext", - "rootcontext", - "stream_context", - "getdictionary", - "setloglevel", - "fastedge", - "class", - "largedictionaryroot", - "extends", - "createcontext", - "context_id", - "u32", - "con" - ] - }, - { - "id": "cdn-examples-large-dictionary-as#when-to-use-getdictionary-vs-getenv", - "heading": "When to use `getDictionary` vs `getEnv`", - "level": 2, - "anchor": "when-to-use-getdictionary-vs-getenv", - "line_start": 224, - "line_end": 234, - "keywords": [ - "use", - "getdictionary", - "getenv", - "function", - "----------", - "name", - "variable", - "value", - "under", - "cases", - "exceed", - "wasi", - "env", - "var", - "size", - "limit", - "environment", - "interface", - "per", - "variable.", - "your", - "app", - "needs", - "read", - "larger", - "values", - "e.g.", - "large", - "json", - "configs", - "pem", - "certificates", - "policy", - "documents", - "calls", - "proxy_dictionary_get", - "bypasses", - "thi" - ] - }, - { - "id": "cdn-examples-large-dictionary-as#what-it-does", - "heading": "What it does", - "level": 2, - "anchor": "what-it-does", - "line_start": 235, - "line_end": 238, - "keywords": [ - "onrequestheaders", - "app", - "reads", - "large_config", - "environment", - "variable", - "using", - "getdictionary", - "logs", - "size", - "adds", - "x-config-size", - "request", - "header", - "upstream", - "see." - ] - }, - { - "id": "cdn-examples-large-dictionary-as#configuration", - "heading": "Configuration", - "level": 2, - "anchor": "configuration", - "line_start": 239, - "line_end": 246, - "keywords": [ - "configuration", - "set", - "following", - "your", - "fastedge", - "application", - "name", - "type", - "description", - "------", - "-------------", - "large_config", - "environment", - "variable", - "large", - "payload", - "e.g.", - "json", - "pem", - "certificate" - ] - }, - { - "id": "cdn-examples-large-dictionary-as#build", - "heading": "Build", - "level": 2, - "anchor": "build", - "line_start": 247, - "line_end": 260, - "keywords": [ - "build", - "pnpm", - "install", - "run", - "asbuild", - "output", - "file", - "description", - "------", - "-------------", - "largedictionary.wasm", - "optimised", - "release", - "binary", - "upload", - "fastedge", - "largedictionary-debug.wasm", - "debug", - "source", - "maps" - ] - }, - { - "id": "cdn-examples-large-dictionary-as#deploy", - "heading": "Deploy", - "level": 2, - "anchor": "deploy", - "line_start": 261, - "line_end": 265, - "keywords": [ - "deploy", - "upload", - "build", - "largedictionary.wasm", - "fastedge", - "portal", - "attach", - "your", - "cdn", - "application.", - "configure", - "large_config", - "environment", - "variable", - "application", - "settings." - ] } ] }, diff --git a/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-ab-testing-as.md b/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-ab-testing-as.md index 0404634..eb12d5d 100644 --- a/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-ab-testing-as.md +++ b/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-ab-testing-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 - updated: 2026-08-17 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> # A/B Testing — AssemblyScript (CDN) @@ -224,3 +224,233 @@ Build scripts use `asc assembly/index.ts --target debug` and `--target release`. - FastEdge CDN application platform overview - FastEdge environment variable configuration - fastedge-test local WASM test runner (for unit-testing cookie parsing and variant assignment logic) + +## Source Material + +### FILE: examples/abTesting/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; +import { + Context, + FilterHeadersStatusValues, + get_property, + log, + LogLevelValues, + registerRootContext, + RootContext, + send_http_response, + set_property, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { + getEnv, + setLogLevel, +} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; +import { getCurrentTime } from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge/utils/runtime"; + +class AbTestingRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); + return new AbTestingContext(context_id, this); + } +} + +class AbTestingContext extends Context { + constructor(context_id: u32, root_context: AbTestingRoot) { + super(context_id, root_context); + } + + onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + const experimentName = getEnv("EXPERIMENT_NAME"); + if (experimentName === "") { + send_http_response( + 500, + "internal server error", + String.UTF8.encode("App misconfigured - EXPERIMENT_NAME must be set"), + [], + ); + return FilterHeadersStatusValues.StopIteration; + } + + const variantAPath = getEnv("VARIANT_A_PATH"); + const variantBPath = getEnv("VARIANT_B_PATH"); + if (variantAPath === "" || variantBPath === "") { + send_http_response( + 500, + "internal server error", + String.UTF8.encode( + "App misconfigured - VARIANT_A_PATH and VARIANT_B_PATH must be set", + ), + [], + ); + return FilterHeadersStatusValues.StopIteration; + } + + // Check for existing experiment cookie + const cookieName = "fe_exp_" + experimentName; + const cookieHeader = stream_context.headers.request.get("Cookie"); + let assignedVariant = this.getCookieValue(cookieHeader, cookieName); + + // Assign variant if not already set + if (assignedVariant !== "A" && assignedVariant !== "B") { + // Use current time as a simple entropy source for 50/50 split + const now = getCurrentTime(); + assignedVariant = now % 2 == 0 ? "A" : "B"; + } + + // Rewrite the request path to the variant path + const pathArrBuf = get_property("request.path"); + if (pathArrBuf.byteLength === 0) { + return FilterHeadersStatusValues.Continue; + } + const originalPath = String.UTF8.decode(pathArrBuf); + const variantPath = assignedVariant === "A" ? variantAPath : variantBPath; + const newPath = variantPath + originalPath; + + // Reconstruct request.url from its decomposed parts rather than splicing + // the path out of the full URL — splicing breaks when the path happens to + // appear inside the host, and it can silently lose the query string. + const schemeBuf = get_property("request.scheme"); + const hostBuf = get_property("request.host"); + if (schemeBuf.byteLength > 0 && hostBuf.byteLength > 0) { + const scheme = String.UTF8.decode(schemeBuf); + const host = String.UTF8.decode(hostBuf); + const queryBuf = get_property("request.query"); + const query = queryBuf.byteLength > 0 ? String.UTF8.decode(queryBuf) : ""; + const newUrl = + scheme + "://" + host + newPath + (query.length > 0 ? "?" + query : ""); + log(LogLevelValues.info, `A/B routing: ${newUrl}`); + set_property("request.url", String.UTF8.encode(newUrl)); + } + + // Add variant header for upstream visibility + stream_context.headers.request.add("X-Experiment", experimentName); + stream_context.headers.request.add("X-Variant", assignedVariant); + + log( + LogLevelValues.info, + `A/B test "${experimentName}": variant ${assignedVariant}, path ${newPath}`, + ); + + return FilterHeadersStatusValues.Continue; + } + + onResponseHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + // Recover the assigned variant from the request header set in onRequestHeaders. + // Instance state (this.variant) does not survive the nginx -> core-proxy hop. + const variant = stream_context.headers.request.get("X-Variant"); + if (variant === "") { + return FilterHeadersStatusValues.Continue; + } + + const experimentName = getEnv("EXPERIMENT_NAME"); + const cookieName = "fe_exp_" + experimentName; + + // Set the experiment cookie so subsequent requests stick to the same variant + stream_context.headers.response.add( + "Set-Cookie", + cookieName + "=" + variant + "; Path=/; Max-Age=86400; SameSite=Lax", + ); + + // Add variant as response header for observability + stream_context.headers.response.add("X-Variant", variant); + + return FilterHeadersStatusValues.Continue; + } + + private getCookieValue(cookieHeader: string, name: string): string { + if (cookieHeader === "") return ""; + const pairs = cookieHeader.split(";"); + const prefix = name + "="; + for (let i = 0; i < pairs.length; i++) { + const pair = pairs[i].trim(); + if (pair.startsWith(prefix)) { + return pair.substring(prefix.length); + } + } + return ""; + } +} + +registerRootContext((context_id: u32) => { + return new AbTestingRoot(context_id); +}, "abTesting"); +``` + + +### FILE: examples/abTesting/package.json + +```json +{ + "name": "fastedge-as-example-ab-testing", + "version": "1.0.0", + "description": "FastEdge AssemblyScript example: A/B Testing — cookie-based traffic splitting at the CDN layer", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + + +### FILE: examples/abTesting/README.md + +``` +[← Back to examples](../README.md) + +# A/B Testing + +This application performs cookie-based A/B traffic splitting at the CDN layer, routing requests to different origin paths based on variant assignment. + +## What it does + +In `onRequestHeaders`, the app: + +1. Checks for an existing experiment cookie (`fe_exp_`). +2. If no cookie is found, assigns the user to variant **A** or **B** (50/50 split). +3. Rewrites the request path by prepending the variant-specific path prefix (e.g. `/variant-a/original/path`). +4. Adds `X-Experiment` and `X-Variant` request headers for upstream visibility. + +In `onResponseHeaders`, the app sets a `Set-Cookie` header to persist the variant assignment for subsequent requests (24-hour TTL). + +> **Note on variant assignment entropy:** New-visitor assignment uses `getCurrentTime() % 2` as a simple 50/50 source. This is illustrative — it is not sticky across two requests that arrive in the same millisecond and is not reproducible in tests. Production A/B implementations typically hash a stable visitor identifier (e.g. client IP or session token) for deterministic, sticky pre-cookie assignment. + +## Configuration + +Set the following environment variables on your FastEdge application: + +| Variable | Example | Description | +|----------|---------|-------------| +| `EXPERIMENT_NAME` | `homepage-redesign` | Name of the experiment (required) | +| `VARIANT_A_PATH` | `/variant-a` | Path prefix for variant A (required) | +| `VARIANT_B_PATH` | `/variant-b` | Path prefix for variant B (required) | + +Your origin server should serve different content at each variant path prefix. + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +|------|-------------| +| `build/abTesting.wasm` | Optimised release binary — upload this to FastEdge | +| `build/abTesting-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/abTesting.wasm` to the FastEdge portal and attach it to your CDN application. Configure the experiment environment variables in the application settings. +``` diff --git a/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-api-key-as.md b/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-api-key-as.md index 4807ec3..2f16ffd 100644 --- a/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-api-key-as.md +++ b/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-api-key-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 - updated: 2026-08-20 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -199,3 +199,168 @@ Upload `build/apiKey.wasm` to the FastEdge portal and attach it to a CDN applica - proxy-wasm-sdk-as reference (full `Context`, `RootContext`, `FilterHeadersStatusValues` API) - FastEdge secrets management (how to set and rotate application secrets) - CDN app deployment guide + +## Source Material + +### FILE: examples/apiKey/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; +import { + Context, + FilterHeadersStatusValues, + HeaderPair, + log, + LogLevelValues, + makeHeaderPair, + registerRootContext, + RootContext, + send_http_response, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { + getSecret, + setLogLevel, +} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; + +const UNAUTHORIZED: u32 = 401; +const FORBIDDEN: u32 = 403; +const INTERNAL_SERVER_ERROR: u32 = 500; + +class ApiKeyRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); + return new ApiKeyContext(context_id, this); + } +} + +class ApiKeyContext extends Context { + constructor(context_id: u32, root_context: ApiKeyRoot) { + super(context_id, root_context); + } + + onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + const expectedKey = getSecret("API_KEY"); + if (expectedKey === "") { + log(LogLevelValues.error, "API_KEY secret not configured"); + send_http_response( + INTERNAL_SERVER_ERROR, + "internal server error", + String.UTF8.encode("App misconfigured"), + [], + ); + return FilterHeadersStatusValues.StopIteration; + } + + const providedKey = stream_context.headers.request.get("X-API-Key"); + + if (providedKey === "") { + const authHeaders = new Array(); + authHeaders.push(makeHeaderPair("WWW-Authenticate", "API-Key")); + send_http_response( + UNAUTHORIZED, + "unauthorized", + String.UTF8.encode("Missing X-API-Key header"), + authHeaders, + ); + return FilterHeadersStatusValues.StopIteration; + } + + if (providedKey !== expectedKey) { + log(LogLevelValues.info, "API key validation failed"); + send_http_response( + FORBIDDEN, + "forbidden", + String.UTF8.encode("Invalid API key"), + [], + ); + return FilterHeadersStatusValues.StopIteration; + } + + // .remove() sets the header value to "" rather than deleting it entirely — + // the upstream will see X-API-Key: "" rather than a missing header. + stream_context.headers.request.remove("X-API-Key"); + + log(LogLevelValues.info, "API key validated successfully"); + return FilterHeadersStatusValues.Continue; + } +} + +registerRootContext((context_id: u32) => { + return new ApiKeyRoot(context_id); +}, "apiKey"); +``` + + +### FILE: examples/apiKey/package.json + +```json +{ + "name": "fastedge-as-example-api-key", + "version": "1.0.0", + "description": "FastEdge AssemblyScript example: API Key — validate X-API-Key header against a secret", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + + +### FILE: examples/apiKey/README.md + +``` +[← Back to examples](../README.md) + +# API Key + +This application validates requests using an `X-API-Key` header checked against a stored secret. + +## What it does + +In `onRequestHeaders`, the app: + +1. Reads the expected API key from the `API_KEY` secret. +2. Checks the `X-API-Key` request header. +3. Returns `401 Unauthorized` if the header is missing. +4. Returns `403 Forbidden` if the key does not match. +5. On success, clears the `X-API-Key` header before forwarding to the upstream origin (proxy-wasm `.remove()` sets the header value to an empty string rather than deleting it). + +This is a simpler alternative to JWT validation when you need basic API authentication without token expiry or claims. + +> **Production note:** The key comparison (`providedKey !== expectedKey`) is not constant-time, which opens a timing side-channel for a high-volume attacker. For production use, replace the comparison with a constant-time HMAC equality check or use the `jwt` example which includes proper cryptographic validation. + +## Configuration + +Set the following on your FastEdge application: + +| Name | Type | Description | +|------|------|-------------| +| `API_KEY` | Secret | The expected API key value | + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +|------|-------------| +| `build/apiKey.wasm` | Optimised release binary — upload this to FastEdge | +| `build/apiKey-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/apiKey.wasm` to the FastEdge portal and attach it to your CDN application. Configure the `API_KEY` secret in the application settings. +``` diff --git a/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-body-as.md b/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-body-as.md index d98a1dc..6cea823 100644 --- a/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-body-as.md +++ b/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-body-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 - updated: 2026-08-20 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -337,3 +337,216 @@ When modifying body content: - host-services-rust (for equivalent Rust patterns) - platform-overview (CDN app lifecycle, hook execution order) - examples-headers-as (header-only manipulation without body buffering) + +## Source Material + +### FILE: examples/body/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; // this exports the required functions for the proxy to interact with us. +import { + BufferTypeValues, + Context, + FilterDataStatusValues, + FilterHeadersStatusValues, + get_buffer_bytes, + get_property, + log, + LogLevelValues, + registerRootContext, + RootContext, + set_buffer_bytes, + set_property, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { setLogLevel } from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; + +class HttpBodyRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); // Set the log level to info - for more logging reduce this to LogLevelValues.debug + return new HttpBody(context_id, this); + } +} + +class HttpBody extends Context { + constructor(context_id: u32, root_context: HttpBodyRoot) { + super(context_id, root_context); + } + + onRequestHeaders( + headers: u32, + end_of_stream: bool + ): FilterHeadersStatusValues { + log(LogLevelValues.debug, "onRequestHeaders >>"); + // Remove the "content-length" header + stream_context.headers.request.remove("content-length"); + return FilterHeadersStatusValues.Continue; + } + + onRequestBody( + body_buffer_length: usize, + end_of_stream: bool + ): FilterDataStatusValues { + log(LogLevelValues.debug, "onRequestBody >>"); + if (!end_of_stream) { + // Wait until the complete body is buffered + return FilterDataStatusValues.StopIterationAndBuffer; + } + + // Retrieve the body from the HttpRequestBody buffer + const bodyBytes = get_buffer_bytes( + BufferTypeValues.HttpRequestBody, + 0, + body_buffer_length + ); + + if (bodyBytes.byteLength > 0) { + const bodyStr = String.UTF8.decode(bodyBytes); + log(LogLevelValues.debug, "onRequestBody >> bodyStr: " + bodyStr); + if (bodyStr.includes("Client")) { + const newBody = `Original message body (${body_buffer_length.toString()} bytes) redacted.\n`; + set_buffer_bytes( + BufferTypeValues.HttpRequestBody, + 0, + body_buffer_length, + String.UTF8.encode(newBody) + ); + } + } + return FilterDataStatusValues.Continue; + } + + onResponseHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + log(LogLevelValues.debug, "onResponseHeaders >>"); + + // Remove "content-length" header as the body size will change + stream_context.headers.response.remove("content-length"); + + // Set "transfer-encoding" to "chunked" + stream_context.headers.response.replace("transfer-encoding", "Chunked"); + + const contentType = stream_context.headers.response.get("content-type"); + if (contentType.length > 0) { + set_property("response.content_type", String.UTF8.encode(contentType)); + } + + return FilterHeadersStatusValues.Continue; + } + + onResponseBody( + body_buffer_length: usize, + end_of_stream: bool + ): FilterDataStatusValues { + log(LogLevelValues.debug, "onResponseBody >>" + end_of_stream.toString()); + + if (!end_of_stream) { + // Wait until the complete body is buffered + return FilterDataStatusValues.StopIterationAndBuffer; + } + + log( + LogLevelValues.debug, + "onResponseBody >> body_buffer_length: " + body_buffer_length.toString() + ); + + // Retrieve the request URL + const urlBytes = get_property("request.url"); + const url = urlBytes.byteLength === 0 ? "" : String.UTF8.decode(urlBytes); + if (url !== "") { + log(LogLevelValues.info, `url=${url}`); + } + + // Retrieve the response content type stored in onResponseHeaders + const contentTypeBytes = get_property("response.content_type"); + const contentType = + contentTypeBytes.byteLength === 0 + ? "" + : String.UTF8.decode(contentTypeBytes); + if (contentType !== "") { + log(LogLevelValues.info, `contentType=${contentType}`); + } + + // Retrieve the body from the HttpRequestBody buffer + const bodyBytes = get_buffer_bytes( + BufferTypeValues.HttpResponseBody, + 0, + body_buffer_length + ); + + if (bodyBytes.byteLength > 0) { + const bodyStr = String.UTF8.decode(bodyBytes); + log(LogLevelValues.info, "onResponseBody >> bodyStr: " + bodyStr); + } + return FilterDataStatusValues.Continue; + } +} + +registerRootContext((context_id: u32) => { + return new HttpBodyRoot(context_id); +}, "httpbody"); +``` + +### FILE: examples/body/package.json + +```json +{ + "name": "fastedge-as-example-body", + "version": "1.0.0", + "description": "FastEdge AssemblyScript example: Body manipulation", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + +### FILE: examples/body/README.md + +``` +[← Back to examples](../README.md) + +# Body + +This application modifies the request and response body using the `onRequestBody` and `onResponseBody` lifecycle hooks. + +## What it does + +1. **`onRequestHeaders`** — removes the `content-length` header, required because the body content will be altered. + +2. **`onRequestBody`** — buffers the full request body then checks whether it contains the word `Client`. If found, the body is replaced with a redaction notice. + +3. **`onResponseHeaders`** — removes `content-length`, sets `transfer-encoding: Chunked`, and captures the `content-type` into a runtime property. + +4. **`onResponseBody`** — logs the request URL, content type, and full response body once the stream is complete. + +This demonstrates the basic flow for body manipulation across all lifecycle hooks. Key points: + +- Headers must be adjusted _before_ modifying the body (`content-length`, `transfer-encoding`). +- The `end_of_stream` flag is checked before processing, allowing the body to be buffered across multiple invocations before acting on it. + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +| ----------------------- | -------------------------------------------------- | +| `build/body.wasm` | Optimised release binary — upload this to FastEdge | +| `build/body-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/body.wasm` to the FastEdge portal and attach it to your CDN application. +``` diff --git a/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-custom-error-pages-as.md b/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-custom-error-pages-as.md index ce71e32..9a690b2 100644 --- a/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-custom-error-pages-as.md +++ b/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-custom-error-pages-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -292,3 +292,239 @@ export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; - CDN app platform overview - host-services-rust reference (for Rust equivalent patterns) - best-practices reference (response body buffering, hook sequencing) + +## Source Material + +### FILE: examples/customErrorPages/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; +import { + Context, + FilterDataStatusValues, + FilterHeadersStatusValues, + get_property, + log, + LogLevelValues, + registerRootContext, + RootContext, + set_buffer_bytes, + BufferTypeValues, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { setLogLevel } from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; + +class ErrorPagesRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); + return new ErrorPagesContext(context_id, this); + } +} + +class ErrorPagesContext extends Context { + constructor(context_id: u32, root_context: ErrorPagesRoot) { + super(context_id, root_context); + } + + onResponseHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + const statusBuf = get_property("response.status"); + if (statusBuf.byteLength < 2) { + return FilterHeadersStatusValues.Continue; + } + + const bytes = Uint8Array.wrap(statusBuf); + const code: u32 = (u32(bytes[0]) << 8) | u32(bytes[1]); + + if (code >= 400 && code < 600) { + stream_context.headers.response.replace("Content-Type", "text/html"); + stream_context.headers.response.remove("Content-Length"); + stream_context.headers.response.replace("Transfer-Encoding", "Chunked"); + + log(LogLevelValues.info, "Error response detected: " + code.toString()); + } + + return FilterHeadersStatusValues.Continue; + } + + onResponseBody( + body_buffer_length: usize, + end_of_stream: bool, + ): FilterDataStatusValues { + // Read response status from property (no instance state between hooks) + const statusBuf = get_property("response.status"); + if (statusBuf.byteLength < 2) { + return FilterDataStatusValues.Continue; + } + + const bytes = Uint8Array.wrap(statusBuf); + const code: u32 = (u32(bytes[0]) << 8) | u32(bytes[1]); + + if (code < 400 || code >= 600) { + return FilterDataStatusValues.Continue; + } + + if (!end_of_stream) { + return FilterDataStatusValues.StopIterationAndBuffer; + } + const title = this.getErrorTitle(code); + const description = this.getErrorDescription(code); + const category = code >= 500 ? "Server Error" : "Client Error"; + + const html = + '' + + '' + + "" + + code.toString() + + " — " + + title + + "" + + "
" + + "

" + + code.toString() + + "

" + + "

" + + title + + "

" + + "

" + + description + + "

" + + "

" + + category + + "

" + + "
"; + + const body = String.UTF8.encode(html); + // Replace the entire original body — pass body_buffer_length as the length + // to replace, not body.byteLength. Otherwise any original bytes beyond the + // new body's length survive at the tail of the response. + set_buffer_bytes( + BufferTypeValues.HttpResponseBody, + 0, + body_buffer_length as u32, + body, + ); + + return FilterDataStatusValues.Continue; + } + + private getErrorTitle(code: u32): string { + if (code == 400) return "Bad Request"; + if (code == 401) return "Unauthorized"; + if (code == 403) return "Forbidden"; + if (code == 404) return "Not Found"; + if (code == 405) return "Method Not Allowed"; + if (code == 408) return "Request Timeout"; + if (code == 429) return "Too Many Requests"; + if (code == 500) return "Internal Server Error"; + if (code == 502) return "Bad Gateway"; + if (code == 503) return "Service Unavailable"; + if (code == 504) return "Gateway Timeout"; + if (code >= 500) return "Server Error"; + return "Error"; + } + + private getErrorDescription(code: u32): string { + if (code == 400) + return "The server could not understand the request due to invalid syntax."; + if (code == 401) + return "You need to authenticate to access this resource."; + if (code == 403) + return "You do not have permission to access this resource."; + if (code == 404) + return "The requested page could not be found. It may have been moved or deleted."; + if (code == 405) + return "The request method is not supported for this resource."; + if (code == 408) + return "The server timed out waiting for the request."; + if (code == 429) + return "You have sent too many requests. Please try again later."; + if (code == 500) + return "The server encountered an unexpected condition that prevented it from fulfilling the request."; + if (code == 502) + return "The server received an invalid response from the upstream server."; + if (code == 503) + return "The server is temporarily unavailable. Please try again later."; + if (code == 504) + return "The server did not receive a timely response from the upstream server."; + if (code >= 500) + return "The server encountered an error processing your request."; + return "An error occurred processing your request."; + } +} + +registerRootContext((context_id: u32) => { + return new ErrorPagesRoot(context_id); +}, "customErrorPages"); +``` + +### FILE: examples/customErrorPages/package.json + +```json +{ + "name": "fastedge-as-example-custom-error-pages", + "version": "1.0.0", + "description": "FastEdge AssemblyScript example: Custom Error Pages — replace 4xx/5xx responses with branded HTML", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + +### FILE: examples/customErrorPages/README.md + +``` +[← Back to examples](../README.md) + +# Custom Error Pages + +This application intercepts 4xx and 5xx error responses and replaces them with clean, branded HTML error pages. + +## What it does + +In `onResponseHeaders`, the app reads the `response.status` property. If it is in the 400-599 range, it sets the `Content-Type` to `text/html` and prepares for body replacement. + +In `onResponseBody`, the app buffers the full response, then replaces the body with a styled HTML page containing: + +- The numeric status code +- A human-readable error title (e.g. "Not Found", "Bad Gateway") +- A description explaining what went wrong +- An error category label ("Client Error" or "Server Error") + +Covers all common HTTP error codes (400, 401, 403, 404, 405, 408, 429, 500, 502, 503, 504) with specific messages and falls back to generic messages for other codes. + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +|------|-------------| +| `build/customErrorPages.wasm` | Optimised release binary — upload this to FastEdge | +| `build/customErrorPages-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/customErrorPages.wasm` to the FastEdge portal and attach it to your CDN application. No environment variables or secrets are required. +``` diff --git a/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-env-secrets-as.md b/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-env-secrets-as.md index 2039ea1..4f89302 100644 --- a/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-env-secrets-as.md +++ b/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-env-secrets-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 - updated: 2026-08-20 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- type: example @@ -225,3 +225,140 @@ import { - examples-headers-cdn-as (request/response header manipulation patterns) - platform-overview (secret management and deployment configuration) - best-practices (logging levels and secret handling guidelines) + +## Source Material + +### FILE: examples/variablesAndSecrets/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; // this exports the required functions for the proxy to interact with us. +import { + Context, + FilterHeadersStatusValues, + log, + LogLevelValues, + registerRootContext, + RootContext, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { + getEnv, + getSecret, + setLogLevel, +} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; + +class VariablesRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); + return new VariablesContext(context_id, this); + } +} + +class VariablesContext extends Context { + constructor(context_id: u32, root_context: VariablesRoot) { + super(context_id, root_context); + } + + onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + const username = getEnv("USERNAME"); + const password = getSecret("PASSWORD"); + + log(LogLevelValues.info, "USERNAME: " + username); + log(LogLevelValues.info, "PASSWORD: [set, length " + password.length.toString() + "]"); + + stream_context.headers.request.add("x-env-username", username); + stream_context.headers.request.add("x-env-password", password); + + return FilterHeadersStatusValues.Continue; + } +} + +registerRootContext((context_id: u32) => { + return new VariablesRoot(context_id); +}, "variablesAndSecrets"); +``` + + +### FILE: examples/variablesAndSecrets/package.json + +```json +{ + "name": "fastedge-as-example-variables-and-secrets", + "version": "0.0.1", + "description": "FastEdge AssemblyScript example: Variables and Secrets", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + + +### FILE: examples/variablesAndSecrets/README.md + +``` +[← Back to examples](../README.md) + +# Variables and Secrets + +This application demonstrates reading environment variables and secrets, then forwarding their values as request headers to the upstream. + +## What it does + +In `onRequestHeaders`, the app: + +1. Reads the `USERNAME` environment variable using `getEnv`. +2. Reads the `PASSWORD` secret using `getSecret`. +3. Logs that both values were retrieved (without logging the secret value itself). +4. Injects them as `x-env-username` and `x-env-password` request headers so the upstream receives them. + +This is useful as a reference for understanding how to access environment variables and secrets within a FastEdge plugin. + +> **Security warning:** Never log secret values verbatim in production. Logs are often persisted and accessible to operators who should not see credential values. This example logs the secret's length rather than its content. Similarly, be deliberate about which upstream systems receive secret values via forwarded headers — limit forwarding to systems that need it. + +## Configuration + +Set the following on your FastEdge application: + +| Name | Type | Description | +| ---------- | -------------------- | -------------------------------------- | +| `USERNAME` | Environment variable | The username value to forward upstream | +| `PASSWORD` | Secret | The password value to forward upstream | + +## Local testing + +The fixture at `fixtures/happy-path.test.json` uses `"dotenv": {"enabled": true}` to load values from `fixtures/.env`. The runner maps `FASTEDGE_VAR_ENV_` to `getEnv("NAME")` and `FASTEDGE_VAR_SECRET_` to `getSecret("NAME")`. + +To test locally with the visual debugger, create `fixtures/.env`: + +``` +FASTEDGE_VAR_ENV_USERNAME=my-username +FASTEDGE_VAR_SECRET_PASSWORD=my-password +``` + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +| -------------------------------------- | -------------------------------------------------- | +| `build/variablesAndSecrets.wasm` | Optimised release binary — upload this to FastEdge | +| `build/variablesAndSecrets-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/variablesAndSecrets.wasm` to the [FastEdge portal](https://portal.gcore.com) and attach it to your CDN application. Configure the `USERNAME` environment variable and the `PASSWORD` secret in the application settings. +``` diff --git a/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-headers-as.md b/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-headers-as.md index 7a52ea5..948a197 100644 --- a/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-headers-as.md +++ b/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-headers-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -66,14 +66,14 @@ registerRootContext((context_id: u32) => { All header operations are accessed via `stream_context.headers.request` or `stream_context.headers.response`. -| Method | Signature | Description | -| -------------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------- | -| `get(name)` | `(name: string) => string` | Returns the value of the named header, or empty string if not present | -| `add(name, value)` | `(name: string, value: string) => void` | Adds a header; multiple calls with the same name produce multiple values | -| `replace(name, value)` | `(name: string, value: string) => void` | Upserts the header value — creates the header if it does not exist (see Known Issues) | -| `remove(name)` | `(name: string) => void` | Removes the header (see Known Issues) | -| `get_headers()` | `() => Headers` (alias: `HeaderPair[]`) | Returns all headers as an array of `{ key: ArrayBuffer, value: ArrayBuffer }` | -| `set_headers(headers)` | `(headers: Headers) => void` | Replaces the full header collection | +| Method | Signature | Description | +| ---------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------- | +| `get(name)` | `(name: string) => string` | Returns the value of the named header, or empty string if not present | +| `add(name, value)` | `(name: string, value: string) => void` | Adds a header; multiple calls with the same name produce multiple values | +| `replace(name, value)` | `(name: string, value: string) => void` | Upserts the header value — creates the header if it does not exist (see Known Issues) | +| `remove(name)` | `(name: string) => void` | Removes the header (see Known Issues) | +| `get_headers()` | `() => Headers` (alias: `HeaderPair[]`) | Returns all headers as an array of `{ key: ArrayBuffer, value: ArrayBuffer }` | +| `set_headers(headers)` | `(headers: Headers) => void` | Replaces the full header collection | ### `Headers` / `HeaderPair` type diff --git a/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-http-call-as.md b/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-http-call-as.md index 95ba36c..d4005b0 100644 --- a/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-http-call-as.md +++ b/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-http-call-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> ## Overview @@ -83,7 +83,9 @@ Reads response headers inside the HTTP call callback. stream_context.headers.http_callback.get(name: string): string ``` -Returns empty string if the header is not present. Import path: `@gcoredev/proxy-wasm-sdk-as/assembly` +Returns empty string if the header is not present. + +Import path: `@gcoredev/proxy-wasm-sdk-as/assembly` --- diff --git a/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-large-dictionary-as.md b/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-large-dictionary-as.md index fe756b2..d0b2d93 100644 --- a/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-large-dictionary-as.md +++ b/plugins/gcore-fastedge-codex/skills/fastedge-docs/reference/cdn/examples-large-dictionary-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> # Large Dictionary — AssemblyScript (CDN) @@ -72,11 +72,15 @@ class LargeDictionaryContext extends Context { } onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + // Use getDictionary for environment variables that may exceed 64KB. + // For normal-sized env vars (< 64KB), use getEnv instead. const config = getDictionary("LARGE_CONFIG"); - // Check for missing key — returns empty string, not null + const size = config.length; log(LogLevelValues.info, "LARGE_CONFIG size: " + size.toString() + " bytes"); + stream_context.headers.request.add("x-config-size", size.toString()); + return FilterHeadersStatusValues.Continue; } } @@ -136,129 +140,3 @@ Upload `build/largeDictionary.wasm` to the FastEdge portal and attach it to your - `getEnv` — standard WASI environment variable access (under 64 KB) - proxy-wasm-sdk-as SDK reference — full list of imports from `@gcoredev/proxy-wasm-sdk-as/assembly` and `@gcoredev/proxy-wasm-sdk-as/assembly/fastedge` - FastEdge CDN app examples (other concepts) — additional CDN app patterns in the fastedge-docs reference - -## Source Material - -### FILE: examples/largeDictionary/assembly/index.ts - -```ts -export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; -import { - Context, - FilterHeadersStatusValues, - log, - LogLevelValues, - registerRootContext, - RootContext, - stream_context, -} from "@gcoredev/proxy-wasm-sdk-as/assembly"; -import { - getDictionary, - setLogLevel, -} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; - -class LargeDictionaryRoot extends RootContext { - createContext(context_id: u32): Context { - setLogLevel(LogLevelValues.info); - return new LargeDictionaryContext(context_id, this); - } -} - -class LargeDictionaryContext extends Context { - constructor(context_id: u32, root_context: LargeDictionaryRoot) { - super(context_id, root_context); - } - - onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { - // Use getDictionary for environment variables that may exceed 64KB. - // For normal-sized env vars (< 64KB), use getEnv instead. - const config = getDictionary("LARGE_CONFIG"); - - const size = config.length; - log(LogLevelValues.info, "LARGE_CONFIG size: " + size.toString() + " bytes"); - - stream_context.headers.request.add("x-config-size", size.toString()); - - return FilterHeadersStatusValues.Continue; - } -} - -registerRootContext((context_id: u32) => { - return new LargeDictionaryRoot(context_id); -}, "largeDictionary"); -``` - - -### FILE: examples/largeDictionary/package.json - -```json -{ - "name": "fastedge-as-example-large-dictionary", - "version": "1.0.0", - "description": "FastEdge AssemblyScript example: Large Dictionary — read env vars exceeding the 64KB WASI limit", - "scripts": { - "asbuild:debug": "asc assembly/index.ts --target debug", - "asbuild:release": "asc assembly/index.ts --target release", - "asbuild": "npm run asbuild:debug && npm run asbuild:release" - }, - "dependencies": { - "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" - }, - "devDependencies": { - "@assemblyscript/wasi-shim": "^0.1.0", - "assemblyscript": "^0.28.9" - } -} -``` - - -### FILE: examples/largeDictionary/README.md - -``` -[← Back to examples](../README.md) - -# Large Dictionary - -This application demonstrates how to read large environment variables (> 64 KB) using the proxy-wasm dictionary API. - -## When to use `getDictionary` vs `getEnv` - -| Function | Use when | -|----------|----------| -| `getEnv(name)` | Variable value is under 64 KB (most cases) | -| `getDictionary(name)` | Variable value may exceed the 64 KB WASI env var size limit | - -The WASI environment variable interface has a **64 KB size limit** per variable. If your app needs to read larger values (e.g. large JSON configs, PEM certificates, policy documents), use `getDictionary` which calls `proxy_dictionary_get` and bypasses this limit. - -For all other environment variable access, prefer `getEnv` as it uses the standard WASI environment interface. - -## What it does - -In `onRequestHeaders`, the app reads the `LARGE_CONFIG` environment variable using `getDictionary`, logs its size, and adds it as an `x-config-size` request header for the upstream to see. - -## Configuration - -Set the following on your FastEdge application: - -| Name | Type | Description | -|------|------|-------------| -| `LARGE_CONFIG` | Environment variable | A large configuration payload (e.g. JSON, PEM certificate) | - -## Build - -```sh -pnpm install -pnpm run asbuild -``` - -Build output: - -| File | Description | -|------|-------------| -| `build/largeDictionary.wasm` | Optimised release binary — upload this to FastEdge | -| `build/largeDictionary-debug.wasm` | Debug binary with source maps | - -## Deploy - -Upload `build/largeDictionary.wasm` to the FastEdge portal and attach it to your CDN application. Configure the `LARGE_CONFIG` environment variable in the application settings. -``` diff --git a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/ab-testing-as.md b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/ab-testing-as.md index 2393c2e..306f924 100644 --- a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/ab-testing-as.md +++ b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/ab-testing-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -222,233 +222,3 @@ pnpm run asbuild - proxy-wasm-sdk-as assembly API reference - FastEdge environment variable configuration - FastEdge CDN application deployment guide - -## Source Material - -### FILE: examples/abTesting/assembly/index.ts - -```ts -export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; -import { - Context, - FilterHeadersStatusValues, - get_property, - log, - LogLevelValues, - registerRootContext, - RootContext, - send_http_response, - set_property, - stream_context, -} from "@gcoredev/proxy-wasm-sdk-as/assembly"; -import { - getEnv, - setLogLevel, -} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; -import { getCurrentTime } from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge/utils/runtime"; - -class AbTestingRoot extends RootContext { - createContext(context_id: u32): Context { - setLogLevel(LogLevelValues.info); - return new AbTestingContext(context_id, this); - } -} - -class AbTestingContext extends Context { - constructor(context_id: u32, root_context: AbTestingRoot) { - super(context_id, root_context); - } - - onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { - const experimentName = getEnv("EXPERIMENT_NAME"); - if (experimentName === "") { - send_http_response( - 500, - "internal server error", - String.UTF8.encode("App misconfigured - EXPERIMENT_NAME must be set"), - [], - ); - return FilterHeadersStatusValues.StopIteration; - } - - const variantAPath = getEnv("VARIANT_A_PATH"); - const variantBPath = getEnv("VARIANT_B_PATH"); - if (variantAPath === "" || variantBPath === "") { - send_http_response( - 500, - "internal server error", - String.UTF8.encode( - "App misconfigured - VARIANT_A_PATH and VARIANT_B_PATH must be set", - ), - [], - ); - return FilterHeadersStatusValues.StopIteration; - } - - // Check for existing experiment cookie - const cookieName = "fe_exp_" + experimentName; - const cookieHeader = stream_context.headers.request.get("Cookie"); - let assignedVariant = this.getCookieValue(cookieHeader, cookieName); - - // Assign variant if not already set - if (assignedVariant !== "A" && assignedVariant !== "B") { - // Use current time as a simple entropy source for 50/50 split - const now = getCurrentTime(); - assignedVariant = now % 2 == 0 ? "A" : "B"; - } - - // Rewrite the request path to the variant path - const pathArrBuf = get_property("request.path"); - if (pathArrBuf.byteLength === 0) { - return FilterHeadersStatusValues.Continue; - } - const originalPath = String.UTF8.decode(pathArrBuf); - const variantPath = assignedVariant === "A" ? variantAPath : variantBPath; - const newPath = variantPath + originalPath; - - // Reconstruct request.url from its decomposed parts rather than splicing - // the path out of the full URL — splicing breaks when the path happens to - // appear inside the host, and it can silently lose the query string. - const schemeBuf = get_property("request.scheme"); - const hostBuf = get_property("request.host"); - if (schemeBuf.byteLength > 0 && hostBuf.byteLength > 0) { - const scheme = String.UTF8.decode(schemeBuf); - const host = String.UTF8.decode(hostBuf); - const queryBuf = get_property("request.query"); - const query = queryBuf.byteLength > 0 ? String.UTF8.decode(queryBuf) : ""; - const newUrl = - scheme + "://" + host + newPath + (query.length > 0 ? "?" + query : ""); - log(LogLevelValues.info, `A/B routing: ${newUrl}`); - set_property("request.url", String.UTF8.encode(newUrl)); - } - - // Add variant header for upstream visibility - stream_context.headers.request.add("X-Experiment", experimentName); - stream_context.headers.request.add("X-Variant", assignedVariant); - - log( - LogLevelValues.info, - `A/B test "${experimentName}": variant ${assignedVariant}, path ${newPath}`, - ); - - return FilterHeadersStatusValues.Continue; - } - - onResponseHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { - // Recover the assigned variant from the request header set in onRequestHeaders. - // Instance state (this.variant) does not survive the nginx -> core-proxy hop. - const variant = stream_context.headers.request.get("X-Variant"); - if (variant === "") { - return FilterHeadersStatusValues.Continue; - } - - const experimentName = getEnv("EXPERIMENT_NAME"); - const cookieName = "fe_exp_" + experimentName; - - // Set the experiment cookie so subsequent requests stick to the same variant - stream_context.headers.response.add( - "Set-Cookie", - cookieName + "=" + variant + "; Path=/; Max-Age=86400; SameSite=Lax", - ); - - // Add variant as response header for observability - stream_context.headers.response.add("X-Variant", variant); - - return FilterHeadersStatusValues.Continue; - } - - private getCookieValue(cookieHeader: string, name: string): string { - if (cookieHeader === "") return ""; - const pairs = cookieHeader.split(";"); - const prefix = name + "="; - for (let i = 0; i < pairs.length; i++) { - const pair = pairs[i].trim(); - if (pair.startsWith(prefix)) { - return pair.substring(prefix.length); - } - } - return ""; - } -} - -registerRootContext((context_id: u32) => { - return new AbTestingRoot(context_id); -}, "abTesting"); -``` - - -### FILE: examples/abTesting/package.json - -```json -{ - "name": "fastedge-as-example-ab-testing", - "version": "1.0.0", - "description": "FastEdge AssemblyScript example: A/B Testing — cookie-based traffic splitting at the CDN layer", - "scripts": { - "asbuild:debug": "asc assembly/index.ts --target debug", - "asbuild:release": "asc assembly/index.ts --target release", - "asbuild": "npm run asbuild:debug && npm run asbuild:release" - }, - "dependencies": { - "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" - }, - "devDependencies": { - "@assemblyscript/wasi-shim": "^0.1.0", - "assemblyscript": "^0.28.9" - } -} -``` - - -### FILE: examples/abTesting/README.md - -``` -[← Back to examples](../README.md) - -# A/B Testing - -This application performs cookie-based A/B traffic splitting at the CDN layer, routing requests to different origin paths based on variant assignment. - -## What it does - -In `onRequestHeaders`, the app: - -1. Checks for an existing experiment cookie (`fe_exp_`). -2. If no cookie is found, assigns the user to variant **A** or **B** (50/50 split). -3. Rewrites the request path by prepending the variant-specific path prefix (e.g. `/variant-a/original/path`). -4. Adds `X-Experiment` and `X-Variant` request headers for upstream visibility. - -In `onResponseHeaders`, the app sets a `Set-Cookie` header to persist the variant assignment for subsequent requests (24-hour TTL). - -> **Note on variant assignment entropy:** New-visitor assignment uses `getCurrentTime() % 2` as a simple 50/50 source. This is illustrative — it is not sticky across two requests that arrive in the same millisecond and is not reproducible in tests. Production A/B implementations typically hash a stable visitor identifier (e.g. client IP or session token) for deterministic, sticky pre-cookie assignment. - -## Configuration - -Set the following environment variables on your FastEdge application: - -| Variable | Example | Description | -|----------|---------|-------------| -| `EXPERIMENT_NAME` | `homepage-redesign` | Name of the experiment (required) | -| `VARIANT_A_PATH` | `/variant-a` | Path prefix for variant A (required) | -| `VARIANT_B_PATH` | `/variant-b` | Path prefix for variant B (required) | - -Your origin server should serve different content at each variant path prefix. - -## Build - -```sh -pnpm install -pnpm run asbuild -``` - -Build output: - -| File | Description | -|------|-------------| -| `build/abTesting.wasm` | Optimised release binary — upload this to FastEdge | -| `build/abTesting-debug.wasm` | Debug binary with source maps | - -## Deploy - -Upload `build/abTesting.wasm` to the FastEdge portal and attach it to your CDN application. Configure the experiment environment variables in the application settings. -``` diff --git a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/auth-jwt-as.md b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/auth-jwt-as.md index 9da636d..a0f2b66 100644 --- a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/auth-jwt-as.md +++ b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/auth-jwt-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -261,6 +261,12 @@ pnpm install pnpm run asbuild ``` +## Deploy + +Upload `build/jwt.wasm` to the FastEdge portal and attach it to your CDN application. Configure the `SECRET` secret variable in the application settings. + +For more on secrets and secret rotation slots, see the FastEdge secrets documentation. + ## See Also - FastEdge secrets documentation (secret variables and rotation slots) diff --git a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/base-as.md b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/base-as.md index 6a43237..bc7b644 100644 --- a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/base-as.md +++ b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/base-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -14,8 +14,8 @@ languages: [assemblyscript] template_origin: cdn-base source_example: proxy-wasm-sdk-as/examples/helloWorld source_repo: proxy-wasm-sdk-as -source_ref: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 -updated: 2026-09-22 +source_ref: 073f583217448aaf25cb98f5d336a0fda26273ba +updated: 2026-09-29 --- # Base Skeleton: CDN AssemblyScript @@ -263,3 +263,118 @@ registerRootContext((context_id: u32) => { - platform-overview (CDN filter pipeline, hook execution order) - examples-headers-cdn-assemblyscript (header manipulation feature blueprint) - examples-body-cdn-assemblyscript (body transformation feature blueprint) + +## Source Material + +### FILE: examples/helloWorld/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; // this exports the required functions for the proxy to interact with us. +import { + Context, + FilterDataStatusValues, + FilterHeadersStatusValues, + log, + LogLevelValues, + registerRootContext, + RootContext, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; + +class HelloWorldRoot extends RootContext { + createContext(context_id: u32): Context { + return new HelloWorld(context_id, this); + } +} + +class HelloWorld extends Context { + constructor(context_id: u32, root_context: HelloWorldRoot) { + super(context_id, root_context); + } + + onRequestHeaders( + headers: u32, + end_of_stream: bool, + ): FilterHeadersStatusValues { + log(LogLevelValues.info, "onRequestHeaders >> Hello World!"); + return FilterHeadersStatusValues.Continue; + } + + onRequestBody( + body_buffer_length: usize, + end_of_stream: bool, + ): FilterDataStatusValues { + log(LogLevelValues.info, "onRequestBody >> Hello World!"); + return FilterDataStatusValues.Continue; + } + + onResponseHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + log(LogLevelValues.info, "onResponseHeaders >> Hello World!"); + return FilterHeadersStatusValues.Continue; + } + + onResponseBody( + body_buffer_length: usize, + end_of_stream: bool, + ): FilterDataStatusValues { + log(LogLevelValues.info, "onResponseBody >> Hello World!"); + return FilterDataStatusValues.Continue; + } +} + +registerRootContext((context_id: u32) => { + return new HelloWorldRoot(context_id); +}, "helloWorld"); +``` + + +### FILE: examples/helloWorld/package.json + +```json +{ + "name": "fastedge-as-example-hello-world", + "version": "1.0.0", + "description": "FastEdge AssemblyScript example: Hello World — minimal CDN app skeleton", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + + +### FILE: examples/helloWorld/asconfig.json + +```json +{ + "extends": "./node_modules/@assemblyscript/wasi-shim/asconfig.json", + "targets": { + "debug": { + "outFile": "build/helloWorld-debug.wasm", + "textFile": "build/helloWorld-debug.wat", + "sourceMap": true, + "debug": true + }, + "release": { + "outFile": "build/helloWorld.wasm", + "textFile": "build/helloWorld.wat", + "sourceMap": true, + "optimizeLevel": 3, + "shrinkLevel": 0, + "converge": false, + "noAssert": false + } + }, + "options": { + "bindings": "esm", + "use": "abort=abort_proc_exit" + } +} +``` diff --git a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/body-as.md b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/body-as.md index 89784fd..6daeb0a 100644 --- a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/body-as.md +++ b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/body-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -352,7 +352,7 @@ Build scripts defined in `package.json`: ## Deploy -Upload `build/body.wasm` via the FastEdge portal and attach it to your CDN application. +Upload `build/body.wasm` to the FastEdge portal and attach it to your CDN application. ## See Also @@ -360,218 +360,3 @@ Upload `build/body.wasm` via the FastEdge portal and attach it to your CDN appli - cdn-base skeleton - FastEdge portal deployment guide - fastedge-test reference (for testing body manipulation logic) - -## Source Material - -### FILE: examples/body/assembly/index.ts - -```ts -export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; // this exports the required functions for the proxy to interact with us. -import { - BufferTypeValues, - Context, - FilterDataStatusValues, - FilterHeadersStatusValues, - get_buffer_bytes, - get_property, - log, - LogLevelValues, - registerRootContext, - RootContext, - set_buffer_bytes, - set_property, - stream_context, -} from "@gcoredev/proxy-wasm-sdk-as/assembly"; -import { setLogLevel } from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; - -class HttpBodyRoot extends RootContext { - createContext(context_id: u32): Context { - setLogLevel(LogLevelValues.info); // Set the log level to info - for more logging reduce this to LogLevelValues.debug - return new HttpBody(context_id, this); - } -} - -class HttpBody extends Context { - constructor(context_id: u32, root_context: HttpBodyRoot) { - super(context_id, root_context); - } - - onRequestHeaders( - headers: u32, - end_of_stream: bool - ): FilterHeadersStatusValues { - log(LogLevelValues.debug, "onRequestHeaders >>"); - // Remove the "content-length" header - stream_context.headers.request.remove("content-length"); - return FilterHeadersStatusValues.Continue; - } - - onRequestBody( - body_buffer_length: usize, - end_of_stream: bool - ): FilterDataStatusValues { - log(LogLevelValues.debug, "onRequestBody >>"); - if (!end_of_stream) { - // Wait until the complete body is buffered - return FilterDataStatusValues.StopIterationAndBuffer; - } - - // Retrieve the body from the HttpRequestBody buffer - const bodyBytes = get_buffer_bytes( - BufferTypeValues.HttpRequestBody, - 0, - body_buffer_length - ); - - if (bodyBytes.byteLength > 0) { - const bodyStr = String.UTF8.decode(bodyBytes); - log(LogLevelValues.debug, "onRequestBody >> bodyStr: " + bodyStr); - if (bodyStr.includes("Client")) { - const newBody = `Original message body (${body_buffer_length.toString()} bytes) redacted.\n`; - set_buffer_bytes( - BufferTypeValues.HttpRequestBody, - 0, - body_buffer_length, - String.UTF8.encode(newBody) - ); - } - } - return FilterDataStatusValues.Continue; - } - - onResponseHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { - log(LogLevelValues.debug, "onResponseHeaders >>"); - - // Remove "content-length" header as the body size will change - stream_context.headers.response.remove("content-length"); - - // Set "transfer-encoding" to "chunked" - stream_context.headers.response.replace("transfer-encoding", "Chunked"); - - const contentType = stream_context.headers.response.get("content-type"); - if (contentType.length > 0) { - set_property("response.content_type", String.UTF8.encode(contentType)); - } - - return FilterHeadersStatusValues.Continue; - } - - onResponseBody( - body_buffer_length: usize, - end_of_stream: bool - ): FilterDataStatusValues { - log(LogLevelValues.debug, "onResponseBody >>" + end_of_stream.toString()); - - if (!end_of_stream) { - // Wait until the complete body is buffered - return FilterDataStatusValues.StopIterationAndBuffer; - } - - log( - LogLevelValues.debug, - "onResponseBody >> body_buffer_length: " + body_buffer_length.toString() - ); - - // Retrieve the request URL - const urlBytes = get_property("request.url"); - const url = urlBytes.byteLength === 0 ? "" : String.UTF8.decode(urlBytes); - if (url !== "") { - log(LogLevelValues.info, `url=${url}`); - } - - // Retrieve the response content type stored in onResponseHeaders - const contentTypeBytes = get_property("response.content_type"); - const contentType = - contentTypeBytes.byteLength === 0 - ? "" - : String.UTF8.decode(contentTypeBytes); - if (contentType !== "") { - log(LogLevelValues.info, `contentType=${contentType}`); - } - - // Retrieve the body from the HttpRequestBody buffer - const bodyBytes = get_buffer_bytes( - BufferTypeValues.HttpResponseBody, - 0, - body_buffer_length - ); - - if (bodyBytes.byteLength > 0) { - const bodyStr = String.UTF8.decode(bodyBytes); - log(LogLevelValues.info, "onResponseBody >> bodyStr: " + bodyStr); - } - return FilterDataStatusValues.Continue; - } -} - -registerRootContext((context_id: u32) => { - return new HttpBodyRoot(context_id); -}, "httpbody"); -``` - - -### FILE: examples/body/package.json - -```json -{ - "name": "fastedge-as-example-body", - "version": "1.0.0", - "description": "FastEdge AssemblyScript example: Body manipulation", - "scripts": { - "asbuild:debug": "asc assembly/index.ts --target debug", - "asbuild:release": "asc assembly/index.ts --target release", - "asbuild": "npm run asbuild:debug && npm run asbuild:release" - }, - "dependencies": { - "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" - }, - "devDependencies": { - "@assemblyscript/wasi-shim": "^0.1.0", - "assemblyscript": "^0.28.9" - } -} -``` - - -### FILE: examples/body/README.md - -``` -[← Back to examples](../README.md) - -# Body - -This application modifies the request and response body using the `onRequestBody` and `onResponseBody` lifecycle hooks. - -## What it does - -1. **`onRequestHeaders`** — removes the `content-length` header, required because the body content will be altered. - -2. **`onRequestBody`** — buffers the full request body then checks whether it contains the word `Client`. If found, the body is replaced with a redaction notice. - -3. **`onResponseHeaders`** — removes `content-length`, sets `transfer-encoding: Chunked`, and captures the `content-type` into a runtime property. - -4. **`onResponseBody`** — logs the request URL, content type, and full response body once the stream is complete. - -This demonstrates the basic flow for body manipulation across all lifecycle hooks. Key points: - -- Headers must be adjusted _before_ modifying the body (`content-length`, `transfer-encoding`). -- The `end_of_stream` flag is checked before processing, allowing the body to be buffered across multiple invocations before acting on it. - -## Build - -```sh -pnpm install -pnpm run asbuild -``` - -Build output: - -| File | Description | -| ----------------------- | -------------------------------------------------- | -| `build/body.wasm` | Optimised release binary — upload this to FastEdge | -| `build/body-debug.wasm` | Debug binary with source maps | - -## Deploy - -Upload `build/body.wasm` to the FastEdge portal and attach it to your CDN application. -``` diff --git a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/custom-error-pages-as.md b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/custom-error-pages-as.md index bb0b1ac..2403635 100644 --- a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/custom-error-pages-as.md +++ b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/custom-error-pages-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 - updated: 2026-08-20 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -324,3 +324,241 @@ Upload `build/customErrorPages.wasm` to the FastEdge portal and attach it to you - CDN base skeleton reference - host-services-rust reference (for Rust equivalent patterns) - FastEdge platform overview + +## Source Material + +### FILE: examples/customErrorPages/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; +import { + Context, + FilterDataStatusValues, + FilterHeadersStatusValues, + get_property, + log, + LogLevelValues, + registerRootContext, + RootContext, + set_buffer_bytes, + BufferTypeValues, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { setLogLevel } from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; + +class ErrorPagesRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); + return new ErrorPagesContext(context_id, this); + } +} + +class ErrorPagesContext extends Context { + constructor(context_id: u32, root_context: ErrorPagesRoot) { + super(context_id, root_context); + } + + onResponseHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + const statusBuf = get_property("response.status"); + if (statusBuf.byteLength < 2) { + return FilterHeadersStatusValues.Continue; + } + + const bytes = Uint8Array.wrap(statusBuf); + const code: u32 = (u32(bytes[0]) << 8) | u32(bytes[1]); + + if (code >= 400 && code < 600) { + stream_context.headers.response.replace("Content-Type", "text/html"); + stream_context.headers.response.remove("Content-Length"); + stream_context.headers.response.replace("Transfer-Encoding", "Chunked"); + + log(LogLevelValues.info, "Error response detected: " + code.toString()); + } + + return FilterHeadersStatusValues.Continue; + } + + onResponseBody( + body_buffer_length: usize, + end_of_stream: bool, + ): FilterDataStatusValues { + // Read response status from property (no instance state between hooks) + const statusBuf = get_property("response.status"); + if (statusBuf.byteLength < 2) { + return FilterDataStatusValues.Continue; + } + + const bytes = Uint8Array.wrap(statusBuf); + const code: u32 = (u32(bytes[0]) << 8) | u32(bytes[1]); + + if (code < 400 || code >= 600) { + return FilterDataStatusValues.Continue; + } + + if (!end_of_stream) { + return FilterDataStatusValues.StopIterationAndBuffer; + } + const title = this.getErrorTitle(code); + const description = this.getErrorDescription(code); + const category = code >= 500 ? "Server Error" : "Client Error"; + + const html = + '' + + '' + + "" + + code.toString() + + " — " + + title + + "" + + "
" + + "

" + + code.toString() + + "

" + + "

" + + title + + "

" + + "

" + + description + + "

" + + "

" + + category + + "

" + + "
"; + + const body = String.UTF8.encode(html); + // Replace the entire original body — pass body_buffer_length as the length + // to replace, not body.byteLength. Otherwise any original bytes beyond the + // new body's length survive at the tail of the response. + set_buffer_bytes( + BufferTypeValues.HttpResponseBody, + 0, + body_buffer_length as u32, + body, + ); + + return FilterDataStatusValues.Continue; + } + + private getErrorTitle(code: u32): string { + if (code == 400) return "Bad Request"; + if (code == 401) return "Unauthorized"; + if (code == 403) return "Forbidden"; + if (code == 404) return "Not Found"; + if (code == 405) return "Method Not Allowed"; + if (code == 408) return "Request Timeout"; + if (code == 429) return "Too Many Requests"; + if (code == 500) return "Internal Server Error"; + if (code == 502) return "Bad Gateway"; + if (code == 503) return "Service Unavailable"; + if (code == 504) return "Gateway Timeout"; + if (code >= 500) return "Server Error"; + return "Error"; + } + + private getErrorDescription(code: u32): string { + if (code == 400) + return "The server could not understand the request due to invalid syntax."; + if (code == 401) + return "You need to authenticate to access this resource."; + if (code == 403) + return "You do not have permission to access this resource."; + if (code == 404) + return "The requested page could not be found. It may have been moved or deleted."; + if (code == 405) + return "The request method is not supported for this resource."; + if (code == 408) + return "The server timed out waiting for the request."; + if (code == 429) + return "You have sent too many requests. Please try again later."; + if (code == 500) + return "The server encountered an unexpected condition that prevented it from fulfilling the request."; + if (code == 502) + return "The server received an invalid response from the upstream server."; + if (code == 503) + return "The server is temporarily unavailable. Please try again later."; + if (code == 504) + return "The server did not receive a timely response from the upstream server."; + if (code >= 500) + return "The server encountered an error processing your request."; + return "An error occurred processing your request."; + } +} + +registerRootContext((context_id: u32) => { + return new ErrorPagesRoot(context_id); +}, "customErrorPages"); +``` + + +### FILE: examples/customErrorPages/package.json + +```json +{ + "name": "fastedge-as-example-custom-error-pages", + "version": "1.0.0", + "description": "FastEdge AssemblyScript example: Custom Error Pages — replace 4xx/5xx responses with branded HTML", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + + +### FILE: examples/customErrorPages/README.md + +``` +[← Back to examples](../README.md) + +# Custom Error Pages + +This application intercepts 4xx and 5xx error responses and replaces them with clean, branded HTML error pages. + +## What it does + +In `onResponseHeaders`, the app reads the `response.status` property. If it is in the 400-599 range, it sets the `Content-Type` to `text/html` and prepares for body replacement. + +In `onResponseBody`, the app buffers the full response, then replaces the body with a styled HTML page containing: + +- The numeric status code +- A human-readable error title (e.g. "Not Found", "Bad Gateway") +- A description explaining what went wrong +- An error category label ("Client Error" or "Server Error") + +Covers all common HTTP error codes (400, 401, 403, 404, 405, 408, 429, 500, 502, 503, 504) with specific messages and falls back to generic messages for other codes. + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +|------|-------------| +| `build/customErrorPages.wasm` | Optimised release binary — upload this to FastEdge | +| `build/customErrorPages-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/customErrorPages.wasm` to the FastEdge portal and attach it to your CDN application. No environment variables or secrets are required. +``` diff --git a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/env-secrets-as.md b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/env-secrets-as.md index 0a75003..8b32b7e 100644 --- a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/env-secrets-as.md +++ b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/env-secrets-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 - updated: 2026-08-20 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -179,3 +179,140 @@ Upload `build/variablesAndSecrets.wasm` to the FastEdge portal and attach it to - cdn-base skeleton (base request/response handling structure) - fastedge-test reference (local WASM test runner and fixture format) - platform-overview reference (FastEdge application configuration and secret management) + +## Source Material + +### FILE: examples/variablesAndSecrets/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; // this exports the required functions for the proxy to interact with us. +import { + Context, + FilterHeadersStatusValues, + log, + LogLevelValues, + registerRootContext, + RootContext, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { + getEnv, + getSecret, + setLogLevel, +} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; + +class VariablesRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); + return new VariablesContext(context_id, this); + } +} + +class VariablesContext extends Context { + constructor(context_id: u32, root_context: VariablesRoot) { + super(context_id, root_context); + } + + onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + const username = getEnv("USERNAME"); + const password = getSecret("PASSWORD"); + + log(LogLevelValues.info, "USERNAME: " + username); + log(LogLevelValues.info, "PASSWORD: [set, length " + password.length.toString() + "]"); + + stream_context.headers.request.add("x-env-username", username); + stream_context.headers.request.add("x-env-password", password); + + return FilterHeadersStatusValues.Continue; + } +} + +registerRootContext((context_id: u32) => { + return new VariablesRoot(context_id); +}, "variablesAndSecrets"); +``` + + +### FILE: examples/variablesAndSecrets/package.json + +```json +{ + "name": "fastedge-as-example-variables-and-secrets", + "version": "0.0.1", + "description": "FastEdge AssemblyScript example: Variables and Secrets", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + + +### FILE: examples/variablesAndSecrets/README.md + +``` +[← Back to examples](../README.md) + +# Variables and Secrets + +This application demonstrates reading environment variables and secrets, then forwarding their values as request headers to the upstream. + +## What it does + +In `onRequestHeaders`, the app: + +1. Reads the `USERNAME` environment variable using `getEnv`. +2. Reads the `PASSWORD` secret using `getSecret`. +3. Logs that both values were retrieved (without logging the secret value itself). +4. Injects them as `x-env-username` and `x-env-password` request headers so the upstream receives them. + +This is useful as a reference for understanding how to access environment variables and secrets within a FastEdge plugin. + +> **Security warning:** Never log secret values verbatim in production. Logs are often persisted and accessible to operators who should not see credential values. This example logs the secret's length rather than its content. Similarly, be deliberate about which upstream systems receive secret values via forwarded headers — limit forwarding to systems that need it. + +## Configuration + +Set the following on your FastEdge application: + +| Name | Type | Description | +| ---------- | -------------------- | -------------------------------------- | +| `USERNAME` | Environment variable | The username value to forward upstream | +| `PASSWORD` | Secret | The password value to forward upstream | + +## Local testing + +The fixture at `fixtures/happy-path.test.json` uses `"dotenv": {"enabled": true}` to load values from `fixtures/.env`. The runner maps `FASTEDGE_VAR_ENV_` to `getEnv("NAME")` and `FASTEDGE_VAR_SECRET_` to `getSecret("NAME")`. + +To test locally with the visual debugger, create `fixtures/.env`: + +``` +FASTEDGE_VAR_ENV_USERNAME=my-username +FASTEDGE_VAR_SECRET_PASSWORD=my-password +``` + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +| -------------------------------------- | -------------------------------------------------- | +| `build/variablesAndSecrets.wasm` | Optimised release binary — upload this to FastEdge | +| `build/variablesAndSecrets-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/variablesAndSecrets.wasm` to the FastEdge portal and attach it to your CDN application. Configure the `USERNAME` environment variable and the `PASSWORD` secret in the application settings. +``` diff --git a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/geo-redirect-as.md b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/geo-redirect-as.md index 35f839a..2fc9227 100644 --- a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/geo-redirect-as.md +++ b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/geo-redirect-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -266,185 +266,3 @@ Build outputs: - platform-overview (runtime properties, Geo-IP data) - deploy skill reference - manage skill reference (environment variable configuration) - -## Source Material - -### FILE: examples/geoRedirect/assembly/index.ts - -```ts -export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; // this exports the required functions for the proxy to interact with us. -import { - Context, - FilterHeadersStatusValues, - get_property, - log, - LogLevelValues, - registerRootContext, - RootContext, - send_http_response, - set_property, -} from "@gcoredev/proxy-wasm-sdk-as/assembly"; -import { - getEnv, - setLogLevel, -} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; - -const BAD_GATEWAY: u32 = 502; -const INTERNAL_SERVER_ERROR: u32 = 500; - -class GeoRedirectRoot extends RootContext { - createContext(context_id: u32): Context { - setLogLevel(LogLevelValues.info); - return new GeoRedirect(context_id, this); - } -} - -class GeoRedirect extends Context { - constructor(context_id: u32, root_context: GeoRedirectRoot) { - super(context_id, root_context); - } - - onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { - log(LogLevelValues.info, "onRequestHeaders >> "); - - const defaultOrigin = getEnv("DEFAULT"); - - if (!defaultOrigin) { - send_http_response( - INTERNAL_SERVER_ERROR, - "internal server error", - String.UTF8.encode("App misconfigured - DEFAULT must be set"), - [], - ); - return FilterHeadersStatusValues.StopIteration; - } - - const countryArrBuf = get_property("request.country"); - if (countryArrBuf.byteLength === 0) { - send_http_response( - BAD_GATEWAY, - "bad gateway", - String.UTF8.encode("Missing country information"), - [], - ); - return FilterHeadersStatusValues.StopIteration; - } - const countryCode = String.UTF8.decode(countryArrBuf); - const countrySpecificOrigin = getEnv(countryCode); - - log( - LogLevelValues.info, - `Country code: ( ${countryCode} ): ${ - countrySpecificOrigin === "" ? "no matching origin" : countrySpecificOrigin - }`, - ); - - const hostArrBuf = get_property("request.host"); - if (hostArrBuf.byteLength > 0) { - const host = String.UTF8.decode(hostArrBuf); - log(LogLevelValues.info, `Provided Host: ${host}`); - } - - const pathArrBuf = get_property("request.path"); - if (pathArrBuf.byteLength === 0) { - send_http_response( - INTERNAL_SERVER_ERROR, - "internal server error", - String.UTF8.encode("Internal server error - no request path"), - [], - ); - return FilterHeadersStatusValues.StopIteration; - } - - const path = String.UTF8.decode(pathArrBuf); - const origin = countrySpecificOrigin === "" ? defaultOrigin : countrySpecificOrigin; - // remove trailing slashes from the origin - const cleanedOrigin = origin.endsWith("/") ? origin.slice(0, -1) : origin; - - const requestUrl = `${cleanedOrigin}${path}`; - - log(LogLevelValues.info, `request-url: ${requestUrl}`); - - set_property("request.url", String.UTF8.encode(requestUrl)); - - return FilterHeadersStatusValues.Continue; - } -} - -registerRootContext((context_id: u32) => { - return new GeoRedirectRoot(context_id); -}, "geoRedirect"); -``` - - -### FILE: examples/geoRedirect/package.json - -```json -{ - "name": "fastedge-as-example-georedirect", - "version": "1.0.0", - "description": "FastEdge AssemblyScript example: Geo Redirect", - "scripts": { - "asbuild:debug": "asc assembly/index.ts --target debug", - "asbuild:release": "asc assembly/index.ts --target release", - "asbuild": "npm run asbuild:debug && npm run asbuild:release" - }, - "dependencies": { - "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" - }, - "devDependencies": { - "@assemblyscript/wasi-shim": "^0.1.0", - "assemblyscript": "^0.28.9" - } -} -``` - - -### FILE: examples/geoRedirect/README.md - -``` -[← Back to examples](../README.md) - -# Geo Redirect - -This application redirects requests to different origin URLs based on the client's country code. - -## What it does - -In `onRequestHeaders`, the app reads the client's country code from the `request.country` runtime property (populated by FastEdge's Geo-IP data) and looks up a matching environment variable by that country code. The `request.url` runtime property is then set to route the upstream fetch to the corresponding origin. - -- If a country-specific origin is configured (e.g. env var `DE=https://de.example.com`), the request is routed there. -- Otherwise it falls back to the `DEFAULT` origin. -- Uses [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country codes. - -> **Routing mechanism:** This is not an HTTP redirect (no `Location` header, no 302 response). Setting `request.url` rewrites the upstream fetch target transparently — the client sees a normal 200 response from the matched origin. - -> **Observability:** The app logs the country code, matched origin, and final request URL at INFO level, visible in the FastEdge application logs. - -## Configuration - -Set the following environment variables on your FastEdge application: - -| Variable | Example | Description | -|----------|---------|-------------| -| `DEFAULT` | `https://origin.example.com` | Fallback origin URL (required) | -| `` | `DE=https://de.example.com` | Per-country origin URL (optional, one per country) | - -## Build - -```sh -pnpm install -pnpm run asbuild -``` - -Build output: - -| File | Description | -|------|-------------| -| `build/geoRedirect.wasm` | Optimised release binary — upload this to FastEdge | -| `build/geoRedirect-debug.wasm` | Debug binary with source maps | - -## Deploy - -Upload `build/geoRedirect.wasm` to the FastEdge portal and attach it to your CDN application. Configure the `DEFAULT` environment variable and any per-country overrides in the application settings. -``` diff --git a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/large-dictionary-as.md b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/large-dictionary-as.md index bb51254..6127ba8 100644 --- a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/large-dictionary-as.md +++ b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/large-dictionary-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -138,3 +138,129 @@ No additional dependencies beyond the cdn-base skeleton. - cdn-base skeleton reference - proxy-wasm-sdk-as SDK reference (AssemblyScript) - platform-overview reference for environment variable configuration + +## Source Material + +### FILE: examples/largeDictionary/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; +import { + Context, + FilterHeadersStatusValues, + log, + LogLevelValues, + registerRootContext, + RootContext, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { + getDictionary, + setLogLevel, +} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; + +class LargeDictionaryRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); + return new LargeDictionaryContext(context_id, this); + } +} + +class LargeDictionaryContext extends Context { + constructor(context_id: u32, root_context: LargeDictionaryRoot) { + super(context_id, root_context); + } + + onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + // Use getDictionary for environment variables that may exceed 64KB. + // For normal-sized env vars (< 64KB), use getEnv instead. + const config = getDictionary("LARGE_CONFIG"); + + const size = config.length; + log(LogLevelValues.info, "LARGE_CONFIG size: " + size.toString() + " bytes"); + + stream_context.headers.request.add("x-config-size", size.toString()); + + return FilterHeadersStatusValues.Continue; + } +} + +registerRootContext((context_id: u32) => { + return new LargeDictionaryRoot(context_id); +}, "largeDictionary"); +``` + + +### FILE: examples/largeDictionary/package.json + +```json +{ + "name": "fastedge-as-example-large-dictionary", + "version": "1.0.0", + "description": "FastEdge AssemblyScript example: Large Dictionary — read env vars exceeding the 64KB WASI limit", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + + +### FILE: examples/largeDictionary/README.md + +``` +[← Back to examples](../README.md) + +# Large Dictionary + +This application demonstrates how to read large environment variables (> 64 KB) using the proxy-wasm dictionary API. + +## When to use `getDictionary` vs `getEnv` + +| Function | Use when | +|----------|----------| +| `getEnv(name)` | Variable value is under 64 KB (most cases) | +| `getDictionary(name)` | Variable value may exceed the 64 KB WASI env var size limit | + +The WASI environment variable interface has a **64 KB size limit** per variable. If your app needs to read larger values (e.g. large JSON configs, PEM certificates, policy documents), use `getDictionary` which calls `proxy_dictionary_get` and bypasses this limit. + +For all other environment variable access, prefer `getEnv` as it uses the standard WASI environment interface. + +## What it does + +In `onRequestHeaders`, the app reads the `LARGE_CONFIG` environment variable using `getDictionary`, logs its size, and adds it as an `x-config-size` request header for the upstream to see. + +## Configuration + +Set the following on your FastEdge application: + +| Name | Type | Description | +|------|------|-------------| +| `LARGE_CONFIG` | Environment variable | A large configuration payload (e.g. JSON, PEM certificate) | + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +|------|-------------| +| `build/largeDictionary.wasm` | Optimised release binary — upload this to FastEdge | +| `build/largeDictionary-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/largeDictionary.wasm` to the FastEdge portal and attach it to your CDN application. Configure the `LARGE_CONFIG` environment variable in the application settings. +``` diff --git a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/properties-as.md b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/properties-as.md index 416db62..f83b7e1 100644 --- a/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/properties-as.md +++ b/plugins/gcore-fastedge-codex/skills/scaffold/reference/cdn/properties-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -529,3 +529,270 @@ Upload `build/properties.wasm` to the FastEdge portal and attach it to your CDN - platform-overview (CDN app request lifecycle, available runtime properties) - host-services reference (get_property, set_property, send_http_response, log) - CDN Properties list (full list of available runtime property paths) + +## Source Material + +### FILE: examples/properties/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; // this exports the required functions for the proxy to interact with us. +import { + Context, + FilterHeadersStatusValues, + get_property, + log, + LogLevelValues, + registerRootContext, + RootContext, + send_http_response, + set_property, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { + setLogLevel, +} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; + +const REQUEST_URI = "request.url"; +const REQUEST_HOST = "request.host"; +const REQUEST_PATH = "request.path"; +const REQUEST_SCHEME = "request.scheme"; +const REQUEST_EXTENSION = "request.extension"; +const REQUEST_QUERY = "request.query"; +const REQUEST_X_REAL_IP = "request.x_real_ip"; +const REQUEST_COUNTRY = "request.country"; +const REQUEST_CITY = "request.city"; + +class PropertiesRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); + return new Properties(context_id, this); + } +} + +class Properties extends Context { + constructor(context_id: u32, root_context: PropertiesRoot) { + super(context_id, root_context); + } + + onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + // Error codes 551–559 identify the absent property via the HTTP response status: + // 551=uri 552=host 553=path 554=scheme 555=extension 556=query 557=x_real_ip 558=country 559=city + if (!this.handleProperty(REQUEST_URI, 551, "uri", "request-uri")) { + return FilterHeadersStatusValues.StopIteration; + } + + // host must be present for upstream routing; validated but not logged or exposed as a response header + if (!this.handleProperty(REQUEST_HOST, 552)) { + return FilterHeadersStatusValues.StopIteration; + } + + if (!this.handleProperty(REQUEST_PATH, 553, "path", "request-path")) { + return FilterHeadersStatusValues.StopIteration; + } + + if (!this.handleProperty(REQUEST_SCHEME, 554, "scheme", "request-scheme")) { + return FilterHeadersStatusValues.StopIteration; + } + + if (!this.handleProperty(REQUEST_EXTENSION, 555, "extension", "request-extension", true)) { + return FilterHeadersStatusValues.StopIteration; + } + + if (!this.handleProperty(REQUEST_QUERY, 556, "query", "request-query", true)) { + return FilterHeadersStatusValues.StopIteration; + } + + if (!this.handleProperty(REQUEST_X_REAL_IP, 557, "client_ip", "request-x-real-ip")) { + return FilterHeadersStatusValues.StopIteration; + } + + if (!this.handleProperty(REQUEST_COUNTRY, 558, "country", "request-country")) { + return FilterHeadersStatusValues.StopIteration; + } + + if (!this.handleProperty(REQUEST_CITY, 559, "city", "request-city")) { + return FilterHeadersStatusValues.StopIteration; + } + + // Handle query parameters + const query = get_property(REQUEST_QUERY); + if (query.byteLength !== 0) { + const queryString = String.UTF8.decode(query); + log(LogLevelValues.info, "query=" + queryString); + const params = queryString + .split("&") + .map>((pair) => pair.split("=")); + + for (let i = 0; i < params.length; i++) { + const param = params[i]; + if (param.length !== 2) { + continue; // Skip invalid query parameters + } + const key = param[0]; + const value = param[1]; + if (key.toLowerCase() === "url") { + log(LogLevelValues.info, `change url to: ${value}`); + set_property(REQUEST_URI, String.UTF8.encode(value)); + } else if (key.toLowerCase() === "host") { + log(LogLevelValues.info, `change host to: ${value}`); + set_property(REQUEST_HOST, String.UTF8.encode(value)); + } else if (key.toLowerCase() === "path") { + log(LogLevelValues.info, `change path to: ${value}`); + set_property(REQUEST_PATH, String.UTF8.encode(value)); + } + } + } + + return FilterHeadersStatusValues.Continue; + } + + // allowEmpty=true for properties that may legitimately be absent (e.g. request.extension + // when the path has no file extension, request.query when there is no query string). + // In that case the property is logged with an empty value and processing continues + // without adding an empty response header. + private handleProperty( + propertyKey: string, + errorCode: u32, + propertyName: string = "", + headerName: string = "", + allowEmpty: boolean = false + ): boolean { + const valueArr = get_property(propertyKey); + if (valueArr.byteLength === 0) { + if (allowEmpty) { + if (propertyName.length > 0) { + log(LogLevelValues.info, "onRequestHeaders >> " + propertyName + ": "); + } + return true; + } + send_http_response( + errorCode, + "internal server error", + String.UTF8.encode("Internal server error"), + [] + ); + return false; + } + const value = String.UTF8.decode(valueArr); + if (propertyName.length > 0) { + log(LogLevelValues.info, "onRequestHeaders >> " + propertyName + ": " + value); + } + if (headerName.length > 0) { + stream_context.headers.response.add(headerName, value); + } + return true; + } +} + +registerRootContext((context_id: u32) => { + return new PropertiesRoot(context_id); +}, "properties"); +``` + + +### FILE: examples/properties/package.json + +```json +{ + "name": "fastedge-as-example-properties", + "version": "1.0.0", + "description": "FastEdge AssemblyScript example: Runtime properties", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + + +### FILE: examples/properties/README.md + +``` +[← Back to examples](../README.md) + +# Properties + +This application reads and logs all known FastEdge runtime properties, exposing them as response headers. + +> Note: This example does not cover all the available properties See the [CDN Properties](https://gcore.com/docs/fastedge/getting-started/cdn-properties) list. + +## What it does + +In `onRequestHeaders`, the app reads the following runtime properties using `get_property()` and adds each as a response header: + +| Property | Response header | +| ------------------- | ---------------------- | +| `request.url` | `request-uri` | +| `request.host` | *(validated only)* | +| `request.path` | `request-path` | +| `request.scheme` | `request-scheme` | +| `request.extension` | `request-extension` | +| `request.query` | `request-query` | +| `request.x_real_ip` | `request-x-real-ip` | +| `request.country` | `request-country` | +| `request.city` | `request-city` | + +`request.host` is validated for presence but not logged or exposed as a header — it must be non-empty for the upstream request to route correctly. + +`request.extension` and `request.query` are optional: if absent, the property is logged with an empty value and processing continues without adding a response header. + +If a required property is absent, the app returns a numeric HTTP status identifying the gap: + +| Status | Property | +|--------|----------| +| 551 | `request.url` | +| 552 | `request.host` | +| 553 | `request.path` | +| 554 | `request.scheme` | +| 555 | `request.extension` *(optional — never triggered)* | +| 556 | `request.query` *(optional — never triggered)* | +| 557 | `request.x_real_ip` | +| 558 | `request.country` | +| 559 | `request.city` | + +The app also supports overriding `request.url`, `request.host`, and `request.path` via query parameters (`url=`, `host=`, `path=`). + +## Expected output + +For a request to `/page.html?test=value` with geo properties populated, the app logs at INFO level: + +``` +onRequestHeaders >> uri: https://example.com/page.html?test=value +onRequestHeaders >> path: /page.html?test=value +onRequestHeaders >> scheme: https +onRequestHeaders >> extension: html +onRequestHeaders >> query: test=value +onRequestHeaders >> client_ip: 203.0.113.1 +onRequestHeaders >> country: LU +onRequestHeaders >> city: Luxembourg +query=test=value +``` + +The corresponding `request-*` response headers are also set. Logs are visible in the FastEdge application logs. + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +| ----------------------------- | -------------------------------------------------- | +| `build/properties.wasm` | Optimised release binary — upload this to FastEdge | +| `build/properties-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/properties.wasm` to the [FastEdge portal](https://portal.gcore.com) and attach it to your CDN application. No environment variables are required. +``` diff --git a/plugins/gcore-fastedge-cursor/.cursor-plugin/plugin.json b/plugins/gcore-fastedge-cursor/.cursor-plugin/plugin.json index bca0623..e7fb541 100644 --- a/plugins/gcore-fastedge-cursor/.cursor-plugin/plugin.json +++ b/plugins/gcore-fastedge-cursor/.cursor-plugin/plugin.json @@ -2,7 +2,7 @@ "name": "gcore-fastedge", "displayName": "Gcore FastEdge", "description": "Build, deploy, and manage serverless WebAssembly applications on Gcore FastEdge — Wasm compute on 210+ global edge PoPs.", - "version": "0.2.11", + "version": "0.2.12", "author": { "name": "Gcore", "email": "support@gcore.com", diff --git a/plugins/gcore-fastedge-cursor/docs-index.json b/plugins/gcore-fastedge-cursor/docs-index.json index 9845658..bd1c17b 100644 --- a/plugins/gcore-fastedge-cursor/docs-index.json +++ b/plugins/gcore-fastedge-cursor/docs-index.json @@ -1,9 +1,9 @@ { "schema_version": "1.0.0", - "generated_at": "2026-09-24T09:24:50.834Z", + "generated_at": "2026-10-02T09:07:59.643Z", "source": { "repo": "fastedge-plugin", - "commit": "503a8fc", + "commit": "0db9306", "pipeline": "sync-reference-docs" }, "topics": [ @@ -528,7 +528,7 @@ { "id": "cdn-examples-ab-testing-as", "title": "A/B Testing — AssemblyScript (CDN)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 updated: 2026-08-17 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-ab-testing-as.md", "skill": "fastedge-docs", "category": "examples", @@ -548,11 +548,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "8e3bb621bc013a0aed7e52122066b417ad62a207", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-08-17", + "2026-09-29", "testing", "cookie-based", "traffic", @@ -562,7 +562,7 @@ "requests", "different" ], - "content_sha256": "9546c33b6d3c8657538a75f442e7738541cdad753fc7b88a1cd759cc036629f0", + "content_sha256": "5fa3790d1f7f070a89f606e6999200868933dc65e36fe612d9a0095c6fe0ca1a", "sections": [ { "id": "cdn-examples-ab-testing-as#introduction", @@ -577,11 +577,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "8e3bb621bc013a0aed7e52122066b417ad62a207", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-08-17", + "2026-09-29", "testing", "assemblyscript", "cdn", @@ -990,6 +990,186 @@ "assignment", "logic" ] + }, + { + "id": "cdn-examples-ab-testing-as#source-material", + "heading": "Source Material", + "level": 2, + "anchor": "source-material", + "line_start": 228, + "line_end": 413, + "keywords": [ + "source", + "material", + "file", + "examples", + "abtesting", + "assembly", + "index.ts", + "export", + "gcoredev", + "proxy-wasm-sdk-as", + "proxy", + "import", + "context", + "filterheadersstatusvalues", + "get_property", + "log", + "loglevelvalues", + "registerrootcontext", + "rootcontext", + "send_http_response", + "set_property", + "stream_context", + "getenv", + "setloglevel", + "fastedge", + "getcurrenttime", + "pro" + ] + }, + { + "id": "cdn-examples-ab-testing-as#what-it-does", + "heading": "What it does", + "level": 2, + "anchor": "what-it-does", + "line_start": 414, + "line_end": 426, + "keywords": [ + "onrequestheaders", + "app", + "checks", + "existing", + "experiment", + "cookie", + "fe_exp_", + "experiment_name", + "found", + "assigns", + "user", + "variant", + "split", + "rewrites", + "request", + "path", + "prepending", + "variant-specific", + "prefix", + "e.g.", + "variant-a", + "original", + "adds", + "x-experiment", + "x-variant", + "headers", + "upstream", + "visibility.", + "onresponseheaders", + "sets", + "set-cookie", + "header", + "persist", + "assignment", + "sub" + ] + }, + { + "id": "cdn-examples-ab-testing-as#configuration", + "heading": "Configuration", + "level": 2, + "anchor": "configuration", + "line_start": 427, + "line_end": 438, + "keywords": [ + "configuration", + "set", + "following", + "environment", + "variables", + "your", + "fastedge", + "application", + "variable", + "example", + "description", + "----------", + "---------", + "-------------", + "experiment_name", + "homepage-redesign", + "name", + "experiment", + "required", + "variant_a_path", + "variant-a", + "path", + "prefix", + "variant", + "variant_b_path", + "variant-b", + "origin", + "server", + "serve", + "different", + "content", + "prefix." + ] + }, + { + "id": "cdn-examples-ab-testing-as#build", + "heading": "Build", + "level": 2, + "anchor": "build", + "line_start": 439, + "line_end": 452, + "keywords": [ + "build", + "pnpm", + "install", + "run", + "asbuild", + "output", + "file", + "description", + "------", + "-------------", + "abtesting.wasm", + "optimised", + "release", + "binary", + "upload", + "fastedge", + "abtesting-debug.wasm", + "debug", + "source", + "maps" + ] + }, + { + "id": "cdn-examples-ab-testing-as#deploy", + "heading": "Deploy", + "level": 2, + "anchor": "deploy", + "line_start": 453, + "line_end": 457, + "keywords": [ + "deploy", + "upload", + "build", + "abtesting.wasm", + "fastedge", + "portal", + "attach", + "your", + "cdn", + "application.", + "configure", + "experiment", + "environment", + "variables", + "application", + "settings." + ] } ] }, @@ -1652,7 +1832,7 @@ { "id": "cdn-examples-api-key-as", "title": "CDN Example: API Key Authentication (AssemblyScript)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 updated: 2026-08-20 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-api-key-as.md", "skill": "fastedge-docs", "category": "examples", @@ -1672,11 +1852,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "8e3bb621bc013a0aed7e52122066b417ad62a207", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-08-20", + "2026-09-29", "---", "type", "example", @@ -1686,7 +1866,7 @@ "api-key-auth", "header-validation" ], - "content_sha256": "d47decc23a11899c2cdd3360355fb8c5e9db6fb48d6b2ca5c90bee4cc5c8ae2b", + "content_sha256": "af7f651d7d617a498e1776ce8c5ccf0e50fb0d53cb7431ecd40ddbbdea8aa5bf", "sections": [ { "id": "cdn-examples-api-key-as#introduction", @@ -1701,11 +1881,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "8e3bb621bc013a0aed7e52122066b417ad62a207", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-08-20", + "2026-09-29", "---", "type", "example", @@ -2060,89 +2240,259 @@ "deployment", "guide" ] - } - ] - }, - { - "id": "cdn-examples-api-key-rust", - "title": "API Key Validation — CDN (Rust)", - "description": "auto-updated: true sources: - id: fastedge-sdk-rust ref: main commit: 6eedcca9d5c0ddd4ff79ca475965393891da2d75 updated: 2026-09-22 -->", - "path": "plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-api-key-rust.md", - "skill": "fastedge-docs", - "category": "examples", - "app_types": [ - "cdn" - ], - "languages": [ - "rust" - ], - "tags": [ - "examples", - "fastedge-docs", - "cdn", - "rust", - "auto-updated", - "true", - "sources", - "fastedge-sdk-rust", - "ref", - "main", - "commit", - "6eedcca9d5c0ddd4ff79ca475965393891da2d75", - "updated", - "2026-09-22", - "api", - "key", - "validation", - "overview", - "validates", - "incoming", - "requests", - "against" - ], - "content_sha256": "6c28a1664d639ebeb12397dea7294bdd293848795b65c88c2f5aab81e2bee614", - "sections": [ - { - "id": "cdn-examples-api-key-rust#introduction", - "heading": "Introduction", - "level": 1, - "anchor": "introduction", - "line_start": 1, - "line_end": 11, - "keywords": [ - "auto-updated", - "true", - "sources", - "fastedge-sdk-rust", - "ref", - "main", - "commit", - "6eedcca9d5c0ddd4ff79ca475965393891da2d75", - "updated", - "2026-09-22", - "api", - "key", - "validation", - "cdn", - "rust" - ] }, { - "id": "cdn-examples-api-key-rust#overview", - "heading": "Overview", + "id": "cdn-examples-api-key-as#source-material", + "heading": "Source Material", "level": 2, - "anchor": "overview", - "line_start": 12, - "line_end": 15, + "anchor": "source-material", + "line_start": 203, + "line_end": 326, "keywords": [ - "overview", - "validates", - "incoming", - "requests", - "against", - "stored", - "secret", - "using", + "source", + "material", + "file", + "examples", + "apikey", + "assembly", + "index.ts", + "export", + "gcoredev", + "proxy-wasm-sdk-as", + "proxy", + "import", + "context", + "filterheadersstatusvalues", + "headerpair", + "log", + "loglevelvalues", + "makeheaderpair", + "registerrootcontext", + "rootcontext", + "send_http_response", + "stream_context", + "getsecret", + "setloglevel", + "fastedge", + "const", + "unauthorized", + "u32", + "401", + "forbidd" + ] + }, + { + "id": "cdn-examples-api-key-as#what-it-does", + "heading": "What it does", + "level": 2, + "anchor": "what-it-does", + "line_start": 327, + "line_end": 340, + "keywords": [ + "onrequestheaders", + "app", + "reads", + "expected", + "api", + "key", + "api_key", + "secret.", + "checks", + "x-api-key", + "request", + "header.", + "returns", + "401", + "unauthorized", + "header", + "missing.", + "403", + "forbidden", + "match.", + "success", + "clears", + "forwarding", + "upstream", + "origin", + "proxy-wasm", + ".remove", + "sets", + "value", + "empty", + "string", + "rather", + "deleting", + "simpler", + "alternative", + "jwt", + "validation", + "you" + ] + }, + { + "id": "cdn-examples-api-key-as#configuration", + "heading": "Configuration", + "level": 2, + "anchor": "configuration", + "line_start": 341, + "line_end": 348, + "keywords": [ + "configuration", + "set", + "following", + "your", + "fastedge", + "application", + "name", + "type", + "description", + "------", + "-------------", + "api_key", + "secret", + "expected", + "api", + "key", + "value" + ] + }, + { + "id": "cdn-examples-api-key-as#build", + "heading": "Build", + "level": 2, + "anchor": "build", + "line_start": 349, + "line_end": 362, + "keywords": [ + "build", + "pnpm", + "install", + "run", + "asbuild", + "output", + "file", + "description", + "------", + "-------------", + "apikey.wasm", + "optimised", + "release", + "binary", + "upload", + "fastedge", + "apikey-debug.wasm", + "debug", + "source", + "maps" + ] + }, + { + "id": "cdn-examples-api-key-as#deploy", + "heading": "Deploy", + "level": 2, + "anchor": "deploy", + "line_start": 363, + "line_end": 367, + "keywords": [ + "deploy", + "upload", + "build", + "apikey.wasm", + "fastedge", + "portal", + "attach", + "your", + "cdn", + "application.", + "configure", + "api_key", + "secret", + "application", + "settings." + ] + } + ] + }, + { + "id": "cdn-examples-api-key-rust", + "title": "API Key Validation — CDN (Rust)", + "description": "auto-updated: true sources: - id: fastedge-sdk-rust ref: main commit: 6eedcca9d5c0ddd4ff79ca475965393891da2d75 updated: 2026-09-22 -->", + "path": "plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-api-key-rust.md", + "skill": "fastedge-docs", + "category": "examples", + "app_types": [ + "cdn" + ], + "languages": [ + "rust" + ], + "tags": [ + "examples", + "fastedge-docs", + "cdn", + "rust", + "auto-updated", + "true", + "sources", + "fastedge-sdk-rust", + "ref", + "main", + "commit", + "6eedcca9d5c0ddd4ff79ca475965393891da2d75", + "updated", + "2026-09-22", + "api", + "key", + "validation", + "overview", + "validates", + "incoming", + "requests", + "against" + ], + "content_sha256": "6c28a1664d639ebeb12397dea7294bdd293848795b65c88c2f5aab81e2bee614", + "sections": [ + { + "id": "cdn-examples-api-key-rust#introduction", + "heading": "Introduction", + "level": 1, + "anchor": "introduction", + "line_start": 1, + "line_end": 11, + "keywords": [ + "auto-updated", + "true", + "sources", + "fastedge-sdk-rust", + "ref", + "main", + "commit", + "6eedcca9d5c0ddd4ff79ca475965393891da2d75", + "updated", + "2026-09-22", + "api", + "key", + "validation", + "cdn", + "rust" + ] + }, + { + "id": "cdn-examples-api-key-rust#overview", + "heading": "Overview", + "level": 2, + "anchor": "overview", + "line_start": 12, + "line_end": 15, + "keywords": [ + "overview", + "validates", + "incoming", + "requests", + "against", + "stored", + "secret", + "using", "x-api-key", "request", "header.", @@ -3428,7 +3778,7 @@ { "id": "cdn-examples-body-as", "title": "Body Manipulation — AssemblyScript (CDN)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 updated: 2026-08-20 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-body-as.md", "skill": "fastedge-docs", "category": "examples", @@ -3448,11 +3798,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "8e3bb621bc013a0aed7e52122066b417ad62a207", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-08-20", + "2026-09-29", "---", "title", "body", @@ -3462,7 +3812,7 @@ "app_type", "languages" ], - "content_sha256": "94226f2f7783dd5978e697ebea97008427e93dbddfd4b033a8c05d74268e9a34", + "content_sha256": "418f834e01924510268a0c8bb73e37ee660382ae3f31079a506b50e37b508188", "sections": [ { "id": "cdn-examples-body-as#introduction", @@ -3477,11 +3827,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "8e3bb621bc013a0aed7e52122066b417ad62a207", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-08-20", + "2026-09-29", "---", "title", "body", @@ -3908,25 +4258,158 @@ "body", "buffering" ] - } - ] - }, - { - "id": "cdn-examples-body-rust", - "title": "CDN Body Inspection and Modification — Rust", - "description": "auto-updated: true sources: - id: fastedge-sdk-rust ref: main commit: 6347a7c2fda0d03e66f1214db5eec041c16801b7 updated: 2026-07-23 -->", - "path": "plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-body-rust.md", - "skill": "fastedge-docs", - "category": "examples", - "app_types": [ - "cdn" - ], - "languages": [ - "rust" - ], - "tags": [ - "examples", - "fastedge-docs", + }, + { + "id": "cdn-examples-body-as#source-material", + "heading": "Source Material", + "level": 2, + "anchor": "source-material", + "line_start": 341, + "line_end": 519, + "keywords": [ + "source", + "material", + "file", + "examples", + "body", + "assembly", + "index.ts", + "export", + "gcoredev", + "proxy-wasm-sdk-as", + "proxy", + "exports", + "required", + "functions", + "interact", + "us.", + "import", + "buffertypevalues", + "context", + "filterdatastatusvalues", + "filterheadersstatusvalues", + "get_buffer_bytes", + "get_property", + "log", + "loglevelvalues", + "registerrootcontext", + "rootcontext", + "set_buffer_bytes", + "set_property", + "stream_context" + ] + }, + { + "id": "cdn-examples-body-as#what-it-does", + "heading": "What it does", + "level": 2, + "anchor": "what-it-does", + "line_start": 520, + "line_end": 534, + "keywords": [ + "onrequestheaders", + "removes", + "content-length", + "header", + "required", + "because", + "body", + "content", + "altered.", + "onrequestbody", + "buffers", + "full", + "request", + "checks", + "whether", + "contains", + "word", + "client", + "found", + "replaced", + "redaction", + "notice.", + "onresponseheaders", + "sets", + "transfer-encoding", + "chunked", + "captures", + "content-type", + "runtime", + "property.", + "onresponsebody", + "logs", + "url" + ] + }, + { + "id": "cdn-examples-body-as#build", + "heading": "Build", + "level": 2, + "anchor": "build", + "line_start": 535, + "line_end": 548, + "keywords": [ + "build", + "pnpm", + "install", + "run", + "asbuild", + "output", + "file", + "description", + "-----------------------", + "--------------------------------------------------", + "body.wasm", + "optimised", + "release", + "binary", + "upload", + "fastedge", + "body-debug.wasm", + "debug", + "source", + "maps" + ] + }, + { + "id": "cdn-examples-body-as#deploy", + "heading": "Deploy", + "level": 2, + "anchor": "deploy", + "line_start": 549, + "line_end": 553, + "keywords": [ + "deploy", + "upload", + "build", + "body.wasm", + "fastedge", + "portal", + "attach", + "your", + "cdn", + "application." + ] + } + ] + }, + { + "id": "cdn-examples-body-rust", + "title": "CDN Body Inspection and Modification — Rust", + "description": "auto-updated: true sources: - id: fastedge-sdk-rust ref: main commit: 6347a7c2fda0d03e66f1214db5eec041c16801b7 updated: 2026-07-23 -->", + "path": "plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-body-rust.md", + "skill": "fastedge-docs", + "category": "examples", + "app_types": [ + "cdn" + ], + "languages": [ + "rust" + ], + "tags": [ + "examples", + "fastedge-docs", "cdn", "rust", "auto-updated", @@ -7692,7 +8175,7 @@ { "id": "cdn-examples-custom-error-pages-as", "title": "Custom Error Pages — AssemblyScript (CDN)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-custom-error-pages-as.md", "skill": "fastedge-docs", "category": "examples", @@ -7712,11 +8195,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -7726,7 +8209,7 @@ "response-headers", "response-body" ], - "content_sha256": "db4273297015e84837b705d18e071b4684410000b7bc9fcf363fe82c1c58ea15", + "content_sha256": "4db5f9268da49b745f237677f4cba2a096cdd53f63cdad4dc043a01f343f9f7d", "sections": [ { "id": "cdn-examples-custom-error-pages-as#introduction", @@ -7741,11 +8224,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -8175,6 +8658,147 @@ "hook", "sequencing" ] + }, + { + "id": "cdn-examples-custom-error-pages-as#source-material", + "heading": "Source Material", + "level": 2, + "anchor": "source-material", + "line_start": 296, + "line_end": 499, + "keywords": [ + "source", + "material", + "file", + "examples", + "customerrorpages", + "assembly", + "index.ts", + "export", + "gcoredev", + "proxy-wasm-sdk-as", + "proxy", + "import", + "context", + "filterdatastatusvalues", + "filterheadersstatusvalues", + "get_property", + "log", + "loglevelvalues", + "registerrootcontext", + "rootcontext", + "set_buffer_bytes", + "buffertypevalues", + "stream_context", + "setloglevel", + "fastedge", + "class", + "errorpagesroot" + ] + }, + { + "id": "cdn-examples-custom-error-pages-as#what-it-does", + "heading": "What it does", + "level": 2, + "anchor": "what-it-does", + "line_start": 500, + "line_end": 512, + "keywords": [ + "onresponseheaders", + "app", + "reads", + "response.status", + "property.", + "400-599", + "range", + "sets", + "content-type", + "text", + "html", + "prepares", + "body", + "replacement.", + "onresponsebody", + "buffers", + "full", + "response", + "replaces", + "styled", + "page", + "containing", + "numeric", + "status", + "code", + "human-readable", + "error", + "title", + "e.g.", + "found", + "bad", + "gateway", + "description", + "explaining", + "went", + "wrong", + "category", + "label", + "client", + "server" + ] + }, + { + "id": "cdn-examples-custom-error-pages-as#build", + "heading": "Build", + "level": 2, + "anchor": "build", + "line_start": 513, + "line_end": 526, + "keywords": [ + "build", + "pnpm", + "install", + "run", + "asbuild", + "output", + "file", + "description", + "------", + "-------------", + "customerrorpages.wasm", + "optimised", + "release", + "binary", + "upload", + "fastedge", + "customerrorpages-debug.wasm", + "debug", + "source", + "maps" + ] + }, + { + "id": "cdn-examples-custom-error-pages-as#deploy", + "heading": "Deploy", + "level": 2, + "anchor": "deploy", + "line_start": 527, + "line_end": 531, + "keywords": [ + "deploy", + "upload", + "build", + "customerrorpages.wasm", + "fastedge", + "portal", + "attach", + "your", + "cdn", + "application.", + "environment", + "variables", + "secrets", + "required." + ] } ] }, @@ -8733,7 +9357,7 @@ { "id": "cdn-examples-env-secrets-as", "title": "Environment Variables and Secrets — CDN (AssemblyScript)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 updated: 2026-08-20 --> --- type: example app_type: cdn languages: [as] capabilities: [env-vars, secrets] ---", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 --> --- type: example app_type: cdn languages: [as] capabilities: [env-vars, secrets] ---", "path": "plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-env-secrets-as.md", "skill": "fastedge-docs", "category": "examples", @@ -8753,11 +9377,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "8e3bb621bc013a0aed7e52122066b417ad62a207", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-08-20", + "2026-09-29", "---", "type", "example", @@ -8768,7 +9392,7 @@ "secrets", "environment" ], - "content_sha256": "0c73ed18f10ce8d8e06a88f54b4d3f23ac0b67968d5c25e974066c47408cfacb", + "content_sha256": "97297fd0847bec88008f332984d685818a0c9bbd39ceb6e7fcfdd36a8310f413", "sections": [ { "id": "cdn-examples-env-secrets-as#introduction", @@ -8783,11 +9407,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "8e3bb621bc013a0aed7e52122066b417ad62a207", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-08-20", + "2026-09-29", "---", "type", "example", @@ -9189,14 +9813,229 @@ "patterns", "platform-overview", "secret", - "management", - "deployment", - "configuration", - "best-practices", - "logging", - "levels", - "handling", - "guidelines" + "management", + "deployment", + "configuration", + "best-practices", + "logging", + "levels", + "handling", + "guidelines" + ] + }, + { + "id": "cdn-examples-env-secrets-as#source-material", + "heading": "Source Material", + "level": 2, + "anchor": "source-material", + "line_start": 229, + "line_end": 313, + "keywords": [ + "source", + "material", + "file", + "examples", + "variablesandsecrets", + "assembly", + "index.ts", + "export", + "gcoredev", + "proxy-wasm-sdk-as", + "proxy", + "exports", + "required", + "functions", + "interact", + "us.", + "import", + "context", + "filterheadersstatusvalues", + "log", + "loglevelvalues", + "registerrootcontext", + "rootcontext", + "stream_context", + "getenv", + "getsecret", + "setloglevel", + "fastedge" + ] + }, + { + "id": "cdn-examples-env-secrets-as#what-it-does", + "heading": "What it does", + "level": 2, + "anchor": "what-it-does", + "line_start": 314, + "line_end": 326, + "keywords": [ + "onrequestheaders", + "app", + "reads", + "username", + "environment", + "variable", + "using", + "getenv", + "password", + "secret", + "getsecret", + "logs", + "values", + "retrieved", + "without", + "logging", + "value", + "itself", + "injects", + "them", + "x-env-username", + "x-env-password", + "request", + "headers", + "upstream", + "receives", + "them.", + "useful", + "reference", + "understanding", + "access", + "variables", + "secrets", + "within", + "fastedge", + "plugin.", + "security", + "warning", + "neve" + ] + }, + { + "id": "cdn-examples-env-secrets-as#configuration", + "heading": "Configuration", + "level": 2, + "anchor": "configuration", + "line_start": 327, + "line_end": 335, + "keywords": [ + "configuration", + "set", + "following", + "your", + "fastedge", + "application", + "name", + "type", + "description", + "----------", + "--------------------", + "--------------------------------------", + "username", + "environment", + "variable", + "value", + "forward", + "upstream", + "password", + "secret" + ] + }, + { + "id": "cdn-examples-env-secrets-as#local-testing", + "heading": "Local testing", + "level": 2, + "anchor": "local-testing", + "line_start": 336, + "line_end": 346, + "keywords": [ + "local", + "testing", + "fixture", + "fixtures", + "happy-path.test.json", + "uses", + "dotenv", + "enabled", + "true", + "load", + "values", + ".env", + "runner", + "maps", + "fastedge_var_env_", + "name", + "getenv", + "fastedge_var_secret_", + "getsecret", + "test", + "locally", + "visual", + "debugger", + "create", + "fastedge_var_env_username", + "my-username", + "fastedge_var_secret_password", + "my-password" + ] + }, + { + "id": "cdn-examples-env-secrets-as#build", + "heading": "Build", + "level": 2, + "anchor": "build", + "line_start": 347, + "line_end": 360, + "keywords": [ + "build", + "pnpm", + "install", + "run", + "asbuild", + "output", + "file", + "description", + "--------------------------------------", + "--------------------------------------------------", + "variablesandsecrets.wasm", + "optimised", + "release", + "binary", + "upload", + "fastedge", + "variablesandsecrets-debug.wasm", + "debug", + "source", + "maps" + ] + }, + { + "id": "cdn-examples-env-secrets-as#deploy", + "heading": "Deploy", + "level": 2, + "anchor": "deploy", + "line_start": 361, + "line_end": 365, + "keywords": [ + "deploy", + "upload", + "build", + "variablesandsecrets.wasm", + "fastedge", + "portal", + "https", + "portal.gcore.com", + "attach", + "your", + "cdn", + "application.", + "configure", + "username", + "environment", + "variable", + "password", + "secret", + "application", + "settings." ] } ] @@ -11900,7 +12739,7 @@ { "id": "cdn-examples-headers-as", "title": "CDN Headers Manipulation — AssemblyScript", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-headers-as.md", "skill": "fastedge-docs", "category": "examples", @@ -11922,11 +12761,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -11936,7 +12775,7 @@ "headers", "request-headers" ], - "content_sha256": "eb268cb0cde0417bb082dbadb140da4f26f837195f2c6d93a907bc1643fc243e", + "content_sha256": "f4799cc1d18e1e473a374b576a6be37324a4d3f5957d0957a136e10508249d0c", "sections": [ { "id": "cdn-examples-headers-as#introduction", @@ -11951,11 +12790,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -12093,11 +12932,11 @@ "method", "signature", "description", - "--------------------------", + "----------------------", "---------------------------------------", - "-----------------------------------------------------------------------------------------------------", + "---------------------------------------------------------------------------------------------", "get", - "nam" + "name" ] }, { @@ -12934,7 +13773,7 @@ { "id": "cdn-examples-http-call-as", "title": "cdn-examples-http-call-as", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-http-call-as.md", "skill": "fastedge-docs", "category": "examples", @@ -12956,11 +13795,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "overview", "httpcall", "dispatches", @@ -12971,7 +13810,7 @@ "fastedge", "proxy-wasm" ], - "content_sha256": "5fd3877e09d4896bd6c69ed4c0cb9cc56dc6cd0d8fadb01c02391285f4b8bce6", + "content_sha256": "534a12e5822a9394bae1f5757847a68ad870ab3f5eb02922c6f2b2cb3235efd9", "sections": [ { "id": "cdn-examples-http-call-as#introduction", @@ -12986,11 +13825,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22" + "2026-09-29" ] }, { @@ -13039,7 +13878,7 @@ "level": 2, "anchor": "api-patterns", "line_start": 14, - "line_end": 101, + "line_end": 103, "keywords": [ "api", "patterns", @@ -13088,8 +13927,8 @@ "heading": "Common Patterns", "level": 2, "anchor": "common-patterns", - "line_start": 102, - "line_end": 286, + "line_start": 104, + "line_end": 288, "keywords": [ "common", "patterns", @@ -13126,8 +13965,8 @@ "heading": "Build", "level": 2, "anchor": "build", - "line_start": 287, - "line_end": 306, + "line_start": 289, + "line_end": 308, "keywords": [ "build", "pnpm", @@ -13173,8 +14012,8 @@ "heading": "Gotchas", "level": 2, "anchor": "gotchas", - "line_start": 307, - "line_end": 318, + "line_start": 309, + "line_end": 320, "keywords": [ "gotchas", "closures", @@ -13218,8 +14057,8 @@ "heading": "Related", "level": 2, "anchor": "related", - "line_start": 319, - "line_end": 324, + "line_start": 321, + "line_end": 326, "keywords": [ "related", "sdk", @@ -15054,7 +15893,7 @@ { "id": "cdn-examples-large-dictionary-as", "title": "Large Dictionary — AssemblyScript (CDN)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-large-dictionary-as.md", "skill": "fastedge-docs", "category": "examples", @@ -15074,11 +15913,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "large", "dictionary", "overview", @@ -15087,7 +15926,7 @@ "environment", "variables" ], - "content_sha256": "06c3e8963a2738ac48fbbfbf6217cded6950009199039727a0ed53963ab8474d", + "content_sha256": "669d8a1223ebb90b395c33eccbec9eed8b6e24c3d6de414d70dfa49bf3cadf0f", "sections": [ { "id": "cdn-examples-large-dictionary-as#introduction", @@ -15102,11 +15941,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "large", "dictionary", "assemblyscript", @@ -15199,7 +16038,7 @@ "level": 2, "anchor": "pattern-read-in-onrequestheaders", "line_start": 45, - "line_end": 88, + "line_end": 92, "keywords": [ "pattern", "read", @@ -15237,8 +16076,8 @@ "heading": "Environment Variables", "level": 2, "anchor": "environment-variables", - "line_start": 89, - "line_end": 94, + "line_start": 93, + "line_end": 98, "keywords": [ "environment", "variables", @@ -15267,8 +16106,8 @@ "heading": "Build", "level": 2, "anchor": "build", - "line_start": 95, - "line_end": 121, + "line_start": 99, + "line_end": 125, "keywords": [ "build", "package", @@ -15306,8 +16145,8 @@ "heading": "Deployment", "level": 2, "anchor": "deployment", - "line_start": 122, - "line_end": 125, + "line_start": 126, + "line_end": 129, "keywords": [ "deployment", "upload", @@ -15332,8 +16171,8 @@ "heading": "Gotchas", "level": 2, "anchor": "gotchas", - "line_start": 126, - "line_end": 133, + "line_start": 130, + "line_end": 137, "keywords": [ "gotchas", "empty", @@ -15382,8 +16221,8 @@ "heading": "See Also", "level": 2, "anchor": "see-also", - "line_start": 134, - "line_end": 139, + "line_start": 138, + "line_end": 143, "keywords": [ "see", "getenv", @@ -15410,205 +16249,6 @@ "patterns", "fastedge-docs" ] - }, - { - "id": "cdn-examples-large-dictionary-as#source-material", - "heading": "Source Material", - "level": 2, - "anchor": "source-material", - "line_start": 140, - "line_end": 223, - "keywords": [ - "source", - "material", - "file", - "examples", - "largedictionary", - "assembly", - "index.ts", - "export", - "gcoredev", - "proxy-wasm-sdk-as", - "proxy", - "import", - "context", - "filterheadersstatusvalues", - "log", - "loglevelvalues", - "registerrootcontext", - "rootcontext", - "stream_context", - "getdictionary", - "setloglevel", - "fastedge", - "class", - "largedictionaryroot", - "extends", - "createcontext", - "context_id", - "u32", - "con" - ] - }, - { - "id": "cdn-examples-large-dictionary-as#when-to-use-getdictionary-vs-getenv", - "heading": "When to use `getDictionary` vs `getEnv`", - "level": 2, - "anchor": "when-to-use-getdictionary-vs-getenv", - "line_start": 224, - "line_end": 234, - "keywords": [ - "use", - "getdictionary", - "getenv", - "function", - "----------", - "name", - "variable", - "value", - "under", - "cases", - "exceed", - "wasi", - "env", - "var", - "size", - "limit", - "environment", - "interface", - "per", - "variable.", - "your", - "app", - "needs", - "read", - "larger", - "values", - "e.g.", - "large", - "json", - "configs", - "pem", - "certificates", - "policy", - "documents", - "calls", - "proxy_dictionary_get", - "bypasses", - "thi" - ] - }, - { - "id": "cdn-examples-large-dictionary-as#what-it-does", - "heading": "What it does", - "level": 2, - "anchor": "what-it-does", - "line_start": 235, - "line_end": 238, - "keywords": [ - "onrequestheaders", - "app", - "reads", - "large_config", - "environment", - "variable", - "using", - "getdictionary", - "logs", - "size", - "adds", - "x-config-size", - "request", - "header", - "upstream", - "see." - ] - }, - { - "id": "cdn-examples-large-dictionary-as#configuration", - "heading": "Configuration", - "level": 2, - "anchor": "configuration", - "line_start": 239, - "line_end": 246, - "keywords": [ - "configuration", - "set", - "following", - "your", - "fastedge", - "application", - "name", - "type", - "description", - "------", - "-------------", - "large_config", - "environment", - "variable", - "large", - "payload", - "e.g.", - "json", - "pem", - "certificate" - ] - }, - { - "id": "cdn-examples-large-dictionary-as#build", - "heading": "Build", - "level": 2, - "anchor": "build", - "line_start": 247, - "line_end": 260, - "keywords": [ - "build", - "pnpm", - "install", - "run", - "asbuild", - "output", - "file", - "description", - "------", - "-------------", - "largedictionary.wasm", - "optimised", - "release", - "binary", - "upload", - "fastedge", - "largedictionary-debug.wasm", - "debug", - "source", - "maps" - ] - }, - { - "id": "cdn-examples-large-dictionary-as#deploy", - "heading": "Deploy", - "level": 2, - "anchor": "deploy", - "line_start": 261, - "line_end": 265, - "keywords": [ - "deploy", - "upload", - "build", - "largedictionary.wasm", - "fastedge", - "portal", - "attach", - "your", - "cdn", - "application.", - "configure", - "large_config", - "environment", - "variable", - "application", - "settings." - ] } ] }, diff --git a/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-ab-testing-as.md b/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-ab-testing-as.md index 0404634..eb12d5d 100644 --- a/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-ab-testing-as.md +++ b/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-ab-testing-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 - updated: 2026-08-17 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> # A/B Testing — AssemblyScript (CDN) @@ -224,3 +224,233 @@ Build scripts use `asc assembly/index.ts --target debug` and `--target release`. - FastEdge CDN application platform overview - FastEdge environment variable configuration - fastedge-test local WASM test runner (for unit-testing cookie parsing and variant assignment logic) + +## Source Material + +### FILE: examples/abTesting/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; +import { + Context, + FilterHeadersStatusValues, + get_property, + log, + LogLevelValues, + registerRootContext, + RootContext, + send_http_response, + set_property, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { + getEnv, + setLogLevel, +} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; +import { getCurrentTime } from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge/utils/runtime"; + +class AbTestingRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); + return new AbTestingContext(context_id, this); + } +} + +class AbTestingContext extends Context { + constructor(context_id: u32, root_context: AbTestingRoot) { + super(context_id, root_context); + } + + onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + const experimentName = getEnv("EXPERIMENT_NAME"); + if (experimentName === "") { + send_http_response( + 500, + "internal server error", + String.UTF8.encode("App misconfigured - EXPERIMENT_NAME must be set"), + [], + ); + return FilterHeadersStatusValues.StopIteration; + } + + const variantAPath = getEnv("VARIANT_A_PATH"); + const variantBPath = getEnv("VARIANT_B_PATH"); + if (variantAPath === "" || variantBPath === "") { + send_http_response( + 500, + "internal server error", + String.UTF8.encode( + "App misconfigured - VARIANT_A_PATH and VARIANT_B_PATH must be set", + ), + [], + ); + return FilterHeadersStatusValues.StopIteration; + } + + // Check for existing experiment cookie + const cookieName = "fe_exp_" + experimentName; + const cookieHeader = stream_context.headers.request.get("Cookie"); + let assignedVariant = this.getCookieValue(cookieHeader, cookieName); + + // Assign variant if not already set + if (assignedVariant !== "A" && assignedVariant !== "B") { + // Use current time as a simple entropy source for 50/50 split + const now = getCurrentTime(); + assignedVariant = now % 2 == 0 ? "A" : "B"; + } + + // Rewrite the request path to the variant path + const pathArrBuf = get_property("request.path"); + if (pathArrBuf.byteLength === 0) { + return FilterHeadersStatusValues.Continue; + } + const originalPath = String.UTF8.decode(pathArrBuf); + const variantPath = assignedVariant === "A" ? variantAPath : variantBPath; + const newPath = variantPath + originalPath; + + // Reconstruct request.url from its decomposed parts rather than splicing + // the path out of the full URL — splicing breaks when the path happens to + // appear inside the host, and it can silently lose the query string. + const schemeBuf = get_property("request.scheme"); + const hostBuf = get_property("request.host"); + if (schemeBuf.byteLength > 0 && hostBuf.byteLength > 0) { + const scheme = String.UTF8.decode(schemeBuf); + const host = String.UTF8.decode(hostBuf); + const queryBuf = get_property("request.query"); + const query = queryBuf.byteLength > 0 ? String.UTF8.decode(queryBuf) : ""; + const newUrl = + scheme + "://" + host + newPath + (query.length > 0 ? "?" + query : ""); + log(LogLevelValues.info, `A/B routing: ${newUrl}`); + set_property("request.url", String.UTF8.encode(newUrl)); + } + + // Add variant header for upstream visibility + stream_context.headers.request.add("X-Experiment", experimentName); + stream_context.headers.request.add("X-Variant", assignedVariant); + + log( + LogLevelValues.info, + `A/B test "${experimentName}": variant ${assignedVariant}, path ${newPath}`, + ); + + return FilterHeadersStatusValues.Continue; + } + + onResponseHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + // Recover the assigned variant from the request header set in onRequestHeaders. + // Instance state (this.variant) does not survive the nginx -> core-proxy hop. + const variant = stream_context.headers.request.get("X-Variant"); + if (variant === "") { + return FilterHeadersStatusValues.Continue; + } + + const experimentName = getEnv("EXPERIMENT_NAME"); + const cookieName = "fe_exp_" + experimentName; + + // Set the experiment cookie so subsequent requests stick to the same variant + stream_context.headers.response.add( + "Set-Cookie", + cookieName + "=" + variant + "; Path=/; Max-Age=86400; SameSite=Lax", + ); + + // Add variant as response header for observability + stream_context.headers.response.add("X-Variant", variant); + + return FilterHeadersStatusValues.Continue; + } + + private getCookieValue(cookieHeader: string, name: string): string { + if (cookieHeader === "") return ""; + const pairs = cookieHeader.split(";"); + const prefix = name + "="; + for (let i = 0; i < pairs.length; i++) { + const pair = pairs[i].trim(); + if (pair.startsWith(prefix)) { + return pair.substring(prefix.length); + } + } + return ""; + } +} + +registerRootContext((context_id: u32) => { + return new AbTestingRoot(context_id); +}, "abTesting"); +``` + + +### FILE: examples/abTesting/package.json + +```json +{ + "name": "fastedge-as-example-ab-testing", + "version": "1.0.0", + "description": "FastEdge AssemblyScript example: A/B Testing — cookie-based traffic splitting at the CDN layer", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + + +### FILE: examples/abTesting/README.md + +``` +[← Back to examples](../README.md) + +# A/B Testing + +This application performs cookie-based A/B traffic splitting at the CDN layer, routing requests to different origin paths based on variant assignment. + +## What it does + +In `onRequestHeaders`, the app: + +1. Checks for an existing experiment cookie (`fe_exp_`). +2. If no cookie is found, assigns the user to variant **A** or **B** (50/50 split). +3. Rewrites the request path by prepending the variant-specific path prefix (e.g. `/variant-a/original/path`). +4. Adds `X-Experiment` and `X-Variant` request headers for upstream visibility. + +In `onResponseHeaders`, the app sets a `Set-Cookie` header to persist the variant assignment for subsequent requests (24-hour TTL). + +> **Note on variant assignment entropy:** New-visitor assignment uses `getCurrentTime() % 2` as a simple 50/50 source. This is illustrative — it is not sticky across two requests that arrive in the same millisecond and is not reproducible in tests. Production A/B implementations typically hash a stable visitor identifier (e.g. client IP or session token) for deterministic, sticky pre-cookie assignment. + +## Configuration + +Set the following environment variables on your FastEdge application: + +| Variable | Example | Description | +|----------|---------|-------------| +| `EXPERIMENT_NAME` | `homepage-redesign` | Name of the experiment (required) | +| `VARIANT_A_PATH` | `/variant-a` | Path prefix for variant A (required) | +| `VARIANT_B_PATH` | `/variant-b` | Path prefix for variant B (required) | + +Your origin server should serve different content at each variant path prefix. + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +|------|-------------| +| `build/abTesting.wasm` | Optimised release binary — upload this to FastEdge | +| `build/abTesting-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/abTesting.wasm` to the FastEdge portal and attach it to your CDN application. Configure the experiment environment variables in the application settings. +``` diff --git a/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-api-key-as.md b/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-api-key-as.md index 4807ec3..2f16ffd 100644 --- a/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-api-key-as.md +++ b/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-api-key-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 - updated: 2026-08-20 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -199,3 +199,168 @@ Upload `build/apiKey.wasm` to the FastEdge portal and attach it to a CDN applica - proxy-wasm-sdk-as reference (full `Context`, `RootContext`, `FilterHeadersStatusValues` API) - FastEdge secrets management (how to set and rotate application secrets) - CDN app deployment guide + +## Source Material + +### FILE: examples/apiKey/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; +import { + Context, + FilterHeadersStatusValues, + HeaderPair, + log, + LogLevelValues, + makeHeaderPair, + registerRootContext, + RootContext, + send_http_response, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { + getSecret, + setLogLevel, +} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; + +const UNAUTHORIZED: u32 = 401; +const FORBIDDEN: u32 = 403; +const INTERNAL_SERVER_ERROR: u32 = 500; + +class ApiKeyRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); + return new ApiKeyContext(context_id, this); + } +} + +class ApiKeyContext extends Context { + constructor(context_id: u32, root_context: ApiKeyRoot) { + super(context_id, root_context); + } + + onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + const expectedKey = getSecret("API_KEY"); + if (expectedKey === "") { + log(LogLevelValues.error, "API_KEY secret not configured"); + send_http_response( + INTERNAL_SERVER_ERROR, + "internal server error", + String.UTF8.encode("App misconfigured"), + [], + ); + return FilterHeadersStatusValues.StopIteration; + } + + const providedKey = stream_context.headers.request.get("X-API-Key"); + + if (providedKey === "") { + const authHeaders = new Array(); + authHeaders.push(makeHeaderPair("WWW-Authenticate", "API-Key")); + send_http_response( + UNAUTHORIZED, + "unauthorized", + String.UTF8.encode("Missing X-API-Key header"), + authHeaders, + ); + return FilterHeadersStatusValues.StopIteration; + } + + if (providedKey !== expectedKey) { + log(LogLevelValues.info, "API key validation failed"); + send_http_response( + FORBIDDEN, + "forbidden", + String.UTF8.encode("Invalid API key"), + [], + ); + return FilterHeadersStatusValues.StopIteration; + } + + // .remove() sets the header value to "" rather than deleting it entirely — + // the upstream will see X-API-Key: "" rather than a missing header. + stream_context.headers.request.remove("X-API-Key"); + + log(LogLevelValues.info, "API key validated successfully"); + return FilterHeadersStatusValues.Continue; + } +} + +registerRootContext((context_id: u32) => { + return new ApiKeyRoot(context_id); +}, "apiKey"); +``` + + +### FILE: examples/apiKey/package.json + +```json +{ + "name": "fastedge-as-example-api-key", + "version": "1.0.0", + "description": "FastEdge AssemblyScript example: API Key — validate X-API-Key header against a secret", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + + +### FILE: examples/apiKey/README.md + +``` +[← Back to examples](../README.md) + +# API Key + +This application validates requests using an `X-API-Key` header checked against a stored secret. + +## What it does + +In `onRequestHeaders`, the app: + +1. Reads the expected API key from the `API_KEY` secret. +2. Checks the `X-API-Key` request header. +3. Returns `401 Unauthorized` if the header is missing. +4. Returns `403 Forbidden` if the key does not match. +5. On success, clears the `X-API-Key` header before forwarding to the upstream origin (proxy-wasm `.remove()` sets the header value to an empty string rather than deleting it). + +This is a simpler alternative to JWT validation when you need basic API authentication without token expiry or claims. + +> **Production note:** The key comparison (`providedKey !== expectedKey`) is not constant-time, which opens a timing side-channel for a high-volume attacker. For production use, replace the comparison with a constant-time HMAC equality check or use the `jwt` example which includes proper cryptographic validation. + +## Configuration + +Set the following on your FastEdge application: + +| Name | Type | Description | +|------|------|-------------| +| `API_KEY` | Secret | The expected API key value | + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +|------|-------------| +| `build/apiKey.wasm` | Optimised release binary — upload this to FastEdge | +| `build/apiKey-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/apiKey.wasm` to the FastEdge portal and attach it to your CDN application. Configure the `API_KEY` secret in the application settings. +``` diff --git a/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-body-as.md b/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-body-as.md index d98a1dc..6cea823 100644 --- a/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-body-as.md +++ b/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-body-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 - updated: 2026-08-20 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -337,3 +337,216 @@ When modifying body content: - host-services-rust (for equivalent Rust patterns) - platform-overview (CDN app lifecycle, hook execution order) - examples-headers-as (header-only manipulation without body buffering) + +## Source Material + +### FILE: examples/body/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; // this exports the required functions for the proxy to interact with us. +import { + BufferTypeValues, + Context, + FilterDataStatusValues, + FilterHeadersStatusValues, + get_buffer_bytes, + get_property, + log, + LogLevelValues, + registerRootContext, + RootContext, + set_buffer_bytes, + set_property, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { setLogLevel } from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; + +class HttpBodyRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); // Set the log level to info - for more logging reduce this to LogLevelValues.debug + return new HttpBody(context_id, this); + } +} + +class HttpBody extends Context { + constructor(context_id: u32, root_context: HttpBodyRoot) { + super(context_id, root_context); + } + + onRequestHeaders( + headers: u32, + end_of_stream: bool + ): FilterHeadersStatusValues { + log(LogLevelValues.debug, "onRequestHeaders >>"); + // Remove the "content-length" header + stream_context.headers.request.remove("content-length"); + return FilterHeadersStatusValues.Continue; + } + + onRequestBody( + body_buffer_length: usize, + end_of_stream: bool + ): FilterDataStatusValues { + log(LogLevelValues.debug, "onRequestBody >>"); + if (!end_of_stream) { + // Wait until the complete body is buffered + return FilterDataStatusValues.StopIterationAndBuffer; + } + + // Retrieve the body from the HttpRequestBody buffer + const bodyBytes = get_buffer_bytes( + BufferTypeValues.HttpRequestBody, + 0, + body_buffer_length + ); + + if (bodyBytes.byteLength > 0) { + const bodyStr = String.UTF8.decode(bodyBytes); + log(LogLevelValues.debug, "onRequestBody >> bodyStr: " + bodyStr); + if (bodyStr.includes("Client")) { + const newBody = `Original message body (${body_buffer_length.toString()} bytes) redacted.\n`; + set_buffer_bytes( + BufferTypeValues.HttpRequestBody, + 0, + body_buffer_length, + String.UTF8.encode(newBody) + ); + } + } + return FilterDataStatusValues.Continue; + } + + onResponseHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + log(LogLevelValues.debug, "onResponseHeaders >>"); + + // Remove "content-length" header as the body size will change + stream_context.headers.response.remove("content-length"); + + // Set "transfer-encoding" to "chunked" + stream_context.headers.response.replace("transfer-encoding", "Chunked"); + + const contentType = stream_context.headers.response.get("content-type"); + if (contentType.length > 0) { + set_property("response.content_type", String.UTF8.encode(contentType)); + } + + return FilterHeadersStatusValues.Continue; + } + + onResponseBody( + body_buffer_length: usize, + end_of_stream: bool + ): FilterDataStatusValues { + log(LogLevelValues.debug, "onResponseBody >>" + end_of_stream.toString()); + + if (!end_of_stream) { + // Wait until the complete body is buffered + return FilterDataStatusValues.StopIterationAndBuffer; + } + + log( + LogLevelValues.debug, + "onResponseBody >> body_buffer_length: " + body_buffer_length.toString() + ); + + // Retrieve the request URL + const urlBytes = get_property("request.url"); + const url = urlBytes.byteLength === 0 ? "" : String.UTF8.decode(urlBytes); + if (url !== "") { + log(LogLevelValues.info, `url=${url}`); + } + + // Retrieve the response content type stored in onResponseHeaders + const contentTypeBytes = get_property("response.content_type"); + const contentType = + contentTypeBytes.byteLength === 0 + ? "" + : String.UTF8.decode(contentTypeBytes); + if (contentType !== "") { + log(LogLevelValues.info, `contentType=${contentType}`); + } + + // Retrieve the body from the HttpRequestBody buffer + const bodyBytes = get_buffer_bytes( + BufferTypeValues.HttpResponseBody, + 0, + body_buffer_length + ); + + if (bodyBytes.byteLength > 0) { + const bodyStr = String.UTF8.decode(bodyBytes); + log(LogLevelValues.info, "onResponseBody >> bodyStr: " + bodyStr); + } + return FilterDataStatusValues.Continue; + } +} + +registerRootContext((context_id: u32) => { + return new HttpBodyRoot(context_id); +}, "httpbody"); +``` + +### FILE: examples/body/package.json + +```json +{ + "name": "fastedge-as-example-body", + "version": "1.0.0", + "description": "FastEdge AssemblyScript example: Body manipulation", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + +### FILE: examples/body/README.md + +``` +[← Back to examples](../README.md) + +# Body + +This application modifies the request and response body using the `onRequestBody` and `onResponseBody` lifecycle hooks. + +## What it does + +1. **`onRequestHeaders`** — removes the `content-length` header, required because the body content will be altered. + +2. **`onRequestBody`** — buffers the full request body then checks whether it contains the word `Client`. If found, the body is replaced with a redaction notice. + +3. **`onResponseHeaders`** — removes `content-length`, sets `transfer-encoding: Chunked`, and captures the `content-type` into a runtime property. + +4. **`onResponseBody`** — logs the request URL, content type, and full response body once the stream is complete. + +This demonstrates the basic flow for body manipulation across all lifecycle hooks. Key points: + +- Headers must be adjusted _before_ modifying the body (`content-length`, `transfer-encoding`). +- The `end_of_stream` flag is checked before processing, allowing the body to be buffered across multiple invocations before acting on it. + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +| ----------------------- | -------------------------------------------------- | +| `build/body.wasm` | Optimised release binary — upload this to FastEdge | +| `build/body-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/body.wasm` to the FastEdge portal and attach it to your CDN application. +``` diff --git a/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-custom-error-pages-as.md b/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-custom-error-pages-as.md index ce71e32..9a690b2 100644 --- a/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-custom-error-pages-as.md +++ b/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-custom-error-pages-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -292,3 +292,239 @@ export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; - CDN app platform overview - host-services-rust reference (for Rust equivalent patterns) - best-practices reference (response body buffering, hook sequencing) + +## Source Material + +### FILE: examples/customErrorPages/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; +import { + Context, + FilterDataStatusValues, + FilterHeadersStatusValues, + get_property, + log, + LogLevelValues, + registerRootContext, + RootContext, + set_buffer_bytes, + BufferTypeValues, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { setLogLevel } from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; + +class ErrorPagesRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); + return new ErrorPagesContext(context_id, this); + } +} + +class ErrorPagesContext extends Context { + constructor(context_id: u32, root_context: ErrorPagesRoot) { + super(context_id, root_context); + } + + onResponseHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + const statusBuf = get_property("response.status"); + if (statusBuf.byteLength < 2) { + return FilterHeadersStatusValues.Continue; + } + + const bytes = Uint8Array.wrap(statusBuf); + const code: u32 = (u32(bytes[0]) << 8) | u32(bytes[1]); + + if (code >= 400 && code < 600) { + stream_context.headers.response.replace("Content-Type", "text/html"); + stream_context.headers.response.remove("Content-Length"); + stream_context.headers.response.replace("Transfer-Encoding", "Chunked"); + + log(LogLevelValues.info, "Error response detected: " + code.toString()); + } + + return FilterHeadersStatusValues.Continue; + } + + onResponseBody( + body_buffer_length: usize, + end_of_stream: bool, + ): FilterDataStatusValues { + // Read response status from property (no instance state between hooks) + const statusBuf = get_property("response.status"); + if (statusBuf.byteLength < 2) { + return FilterDataStatusValues.Continue; + } + + const bytes = Uint8Array.wrap(statusBuf); + const code: u32 = (u32(bytes[0]) << 8) | u32(bytes[1]); + + if (code < 400 || code >= 600) { + return FilterDataStatusValues.Continue; + } + + if (!end_of_stream) { + return FilterDataStatusValues.StopIterationAndBuffer; + } + const title = this.getErrorTitle(code); + const description = this.getErrorDescription(code); + const category = code >= 500 ? "Server Error" : "Client Error"; + + const html = + '' + + '' + + "" + + code.toString() + + " — " + + title + + "" + + "
" + + "

" + + code.toString() + + "

" + + "

" + + title + + "

" + + "

" + + description + + "

" + + "

" + + category + + "

" + + "
"; + + const body = String.UTF8.encode(html); + // Replace the entire original body — pass body_buffer_length as the length + // to replace, not body.byteLength. Otherwise any original bytes beyond the + // new body's length survive at the tail of the response. + set_buffer_bytes( + BufferTypeValues.HttpResponseBody, + 0, + body_buffer_length as u32, + body, + ); + + return FilterDataStatusValues.Continue; + } + + private getErrorTitle(code: u32): string { + if (code == 400) return "Bad Request"; + if (code == 401) return "Unauthorized"; + if (code == 403) return "Forbidden"; + if (code == 404) return "Not Found"; + if (code == 405) return "Method Not Allowed"; + if (code == 408) return "Request Timeout"; + if (code == 429) return "Too Many Requests"; + if (code == 500) return "Internal Server Error"; + if (code == 502) return "Bad Gateway"; + if (code == 503) return "Service Unavailable"; + if (code == 504) return "Gateway Timeout"; + if (code >= 500) return "Server Error"; + return "Error"; + } + + private getErrorDescription(code: u32): string { + if (code == 400) + return "The server could not understand the request due to invalid syntax."; + if (code == 401) + return "You need to authenticate to access this resource."; + if (code == 403) + return "You do not have permission to access this resource."; + if (code == 404) + return "The requested page could not be found. It may have been moved or deleted."; + if (code == 405) + return "The request method is not supported for this resource."; + if (code == 408) + return "The server timed out waiting for the request."; + if (code == 429) + return "You have sent too many requests. Please try again later."; + if (code == 500) + return "The server encountered an unexpected condition that prevented it from fulfilling the request."; + if (code == 502) + return "The server received an invalid response from the upstream server."; + if (code == 503) + return "The server is temporarily unavailable. Please try again later."; + if (code == 504) + return "The server did not receive a timely response from the upstream server."; + if (code >= 500) + return "The server encountered an error processing your request."; + return "An error occurred processing your request."; + } +} + +registerRootContext((context_id: u32) => { + return new ErrorPagesRoot(context_id); +}, "customErrorPages"); +``` + +### FILE: examples/customErrorPages/package.json + +```json +{ + "name": "fastedge-as-example-custom-error-pages", + "version": "1.0.0", + "description": "FastEdge AssemblyScript example: Custom Error Pages — replace 4xx/5xx responses with branded HTML", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + +### FILE: examples/customErrorPages/README.md + +``` +[← Back to examples](../README.md) + +# Custom Error Pages + +This application intercepts 4xx and 5xx error responses and replaces them with clean, branded HTML error pages. + +## What it does + +In `onResponseHeaders`, the app reads the `response.status` property. If it is in the 400-599 range, it sets the `Content-Type` to `text/html` and prepares for body replacement. + +In `onResponseBody`, the app buffers the full response, then replaces the body with a styled HTML page containing: + +- The numeric status code +- A human-readable error title (e.g. "Not Found", "Bad Gateway") +- A description explaining what went wrong +- An error category label ("Client Error" or "Server Error") + +Covers all common HTTP error codes (400, 401, 403, 404, 405, 408, 429, 500, 502, 503, 504) with specific messages and falls back to generic messages for other codes. + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +|------|-------------| +| `build/customErrorPages.wasm` | Optimised release binary — upload this to FastEdge | +| `build/customErrorPages-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/customErrorPages.wasm` to the FastEdge portal and attach it to your CDN application. No environment variables or secrets are required. +``` diff --git a/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-env-secrets-as.md b/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-env-secrets-as.md index 2039ea1..4f89302 100644 --- a/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-env-secrets-as.md +++ b/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-env-secrets-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 - updated: 2026-08-20 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- type: example @@ -225,3 +225,140 @@ import { - examples-headers-cdn-as (request/response header manipulation patterns) - platform-overview (secret management and deployment configuration) - best-practices (logging levels and secret handling guidelines) + +## Source Material + +### FILE: examples/variablesAndSecrets/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; // this exports the required functions for the proxy to interact with us. +import { + Context, + FilterHeadersStatusValues, + log, + LogLevelValues, + registerRootContext, + RootContext, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { + getEnv, + getSecret, + setLogLevel, +} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; + +class VariablesRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); + return new VariablesContext(context_id, this); + } +} + +class VariablesContext extends Context { + constructor(context_id: u32, root_context: VariablesRoot) { + super(context_id, root_context); + } + + onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + const username = getEnv("USERNAME"); + const password = getSecret("PASSWORD"); + + log(LogLevelValues.info, "USERNAME: " + username); + log(LogLevelValues.info, "PASSWORD: [set, length " + password.length.toString() + "]"); + + stream_context.headers.request.add("x-env-username", username); + stream_context.headers.request.add("x-env-password", password); + + return FilterHeadersStatusValues.Continue; + } +} + +registerRootContext((context_id: u32) => { + return new VariablesRoot(context_id); +}, "variablesAndSecrets"); +``` + + +### FILE: examples/variablesAndSecrets/package.json + +```json +{ + "name": "fastedge-as-example-variables-and-secrets", + "version": "0.0.1", + "description": "FastEdge AssemblyScript example: Variables and Secrets", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + + +### FILE: examples/variablesAndSecrets/README.md + +``` +[← Back to examples](../README.md) + +# Variables and Secrets + +This application demonstrates reading environment variables and secrets, then forwarding their values as request headers to the upstream. + +## What it does + +In `onRequestHeaders`, the app: + +1. Reads the `USERNAME` environment variable using `getEnv`. +2. Reads the `PASSWORD` secret using `getSecret`. +3. Logs that both values were retrieved (without logging the secret value itself). +4. Injects them as `x-env-username` and `x-env-password` request headers so the upstream receives them. + +This is useful as a reference for understanding how to access environment variables and secrets within a FastEdge plugin. + +> **Security warning:** Never log secret values verbatim in production. Logs are often persisted and accessible to operators who should not see credential values. This example logs the secret's length rather than its content. Similarly, be deliberate about which upstream systems receive secret values via forwarded headers — limit forwarding to systems that need it. + +## Configuration + +Set the following on your FastEdge application: + +| Name | Type | Description | +| ---------- | -------------------- | -------------------------------------- | +| `USERNAME` | Environment variable | The username value to forward upstream | +| `PASSWORD` | Secret | The password value to forward upstream | + +## Local testing + +The fixture at `fixtures/happy-path.test.json` uses `"dotenv": {"enabled": true}` to load values from `fixtures/.env`. The runner maps `FASTEDGE_VAR_ENV_` to `getEnv("NAME")` and `FASTEDGE_VAR_SECRET_` to `getSecret("NAME")`. + +To test locally with the visual debugger, create `fixtures/.env`: + +``` +FASTEDGE_VAR_ENV_USERNAME=my-username +FASTEDGE_VAR_SECRET_PASSWORD=my-password +``` + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +| -------------------------------------- | -------------------------------------------------- | +| `build/variablesAndSecrets.wasm` | Optimised release binary — upload this to FastEdge | +| `build/variablesAndSecrets-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/variablesAndSecrets.wasm` to the [FastEdge portal](https://portal.gcore.com) and attach it to your CDN application. Configure the `USERNAME` environment variable and the `PASSWORD` secret in the application settings. +``` diff --git a/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-headers-as.md b/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-headers-as.md index 7a52ea5..948a197 100644 --- a/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-headers-as.md +++ b/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-headers-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -66,14 +66,14 @@ registerRootContext((context_id: u32) => { All header operations are accessed via `stream_context.headers.request` or `stream_context.headers.response`. -| Method | Signature | Description | -| -------------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------- | -| `get(name)` | `(name: string) => string` | Returns the value of the named header, or empty string if not present | -| `add(name, value)` | `(name: string, value: string) => void` | Adds a header; multiple calls with the same name produce multiple values | -| `replace(name, value)` | `(name: string, value: string) => void` | Upserts the header value — creates the header if it does not exist (see Known Issues) | -| `remove(name)` | `(name: string) => void` | Removes the header (see Known Issues) | -| `get_headers()` | `() => Headers` (alias: `HeaderPair[]`) | Returns all headers as an array of `{ key: ArrayBuffer, value: ArrayBuffer }` | -| `set_headers(headers)` | `(headers: Headers) => void` | Replaces the full header collection | +| Method | Signature | Description | +| ---------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------- | +| `get(name)` | `(name: string) => string` | Returns the value of the named header, or empty string if not present | +| `add(name, value)` | `(name: string, value: string) => void` | Adds a header; multiple calls with the same name produce multiple values | +| `replace(name, value)` | `(name: string, value: string) => void` | Upserts the header value — creates the header if it does not exist (see Known Issues) | +| `remove(name)` | `(name: string) => void` | Removes the header (see Known Issues) | +| `get_headers()` | `() => Headers` (alias: `HeaderPair[]`) | Returns all headers as an array of `{ key: ArrayBuffer, value: ArrayBuffer }` | +| `set_headers(headers)` | `(headers: Headers) => void` | Replaces the full header collection | ### `Headers` / `HeaderPair` type diff --git a/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-http-call-as.md b/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-http-call-as.md index 95ba36c..d4005b0 100644 --- a/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-http-call-as.md +++ b/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-http-call-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> ## Overview @@ -83,7 +83,9 @@ Reads response headers inside the HTTP call callback. stream_context.headers.http_callback.get(name: string): string ``` -Returns empty string if the header is not present. Import path: `@gcoredev/proxy-wasm-sdk-as/assembly` +Returns empty string if the header is not present. + +Import path: `@gcoredev/proxy-wasm-sdk-as/assembly` --- diff --git a/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-large-dictionary-as.md b/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-large-dictionary-as.md index fe756b2..d0b2d93 100644 --- a/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-large-dictionary-as.md +++ b/plugins/gcore-fastedge-cursor/skills/fastedge-docs/reference/cdn/examples-large-dictionary-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> # Large Dictionary — AssemblyScript (CDN) @@ -72,11 +72,15 @@ class LargeDictionaryContext extends Context { } onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + // Use getDictionary for environment variables that may exceed 64KB. + // For normal-sized env vars (< 64KB), use getEnv instead. const config = getDictionary("LARGE_CONFIG"); - // Check for missing key — returns empty string, not null + const size = config.length; log(LogLevelValues.info, "LARGE_CONFIG size: " + size.toString() + " bytes"); + stream_context.headers.request.add("x-config-size", size.toString()); + return FilterHeadersStatusValues.Continue; } } @@ -136,129 +140,3 @@ Upload `build/largeDictionary.wasm` to the FastEdge portal and attach it to your - `getEnv` — standard WASI environment variable access (under 64 KB) - proxy-wasm-sdk-as SDK reference — full list of imports from `@gcoredev/proxy-wasm-sdk-as/assembly` and `@gcoredev/proxy-wasm-sdk-as/assembly/fastedge` - FastEdge CDN app examples (other concepts) — additional CDN app patterns in the fastedge-docs reference - -## Source Material - -### FILE: examples/largeDictionary/assembly/index.ts - -```ts -export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; -import { - Context, - FilterHeadersStatusValues, - log, - LogLevelValues, - registerRootContext, - RootContext, - stream_context, -} from "@gcoredev/proxy-wasm-sdk-as/assembly"; -import { - getDictionary, - setLogLevel, -} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; - -class LargeDictionaryRoot extends RootContext { - createContext(context_id: u32): Context { - setLogLevel(LogLevelValues.info); - return new LargeDictionaryContext(context_id, this); - } -} - -class LargeDictionaryContext extends Context { - constructor(context_id: u32, root_context: LargeDictionaryRoot) { - super(context_id, root_context); - } - - onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { - // Use getDictionary for environment variables that may exceed 64KB. - // For normal-sized env vars (< 64KB), use getEnv instead. - const config = getDictionary("LARGE_CONFIG"); - - const size = config.length; - log(LogLevelValues.info, "LARGE_CONFIG size: " + size.toString() + " bytes"); - - stream_context.headers.request.add("x-config-size", size.toString()); - - return FilterHeadersStatusValues.Continue; - } -} - -registerRootContext((context_id: u32) => { - return new LargeDictionaryRoot(context_id); -}, "largeDictionary"); -``` - - -### FILE: examples/largeDictionary/package.json - -```json -{ - "name": "fastedge-as-example-large-dictionary", - "version": "1.0.0", - "description": "FastEdge AssemblyScript example: Large Dictionary — read env vars exceeding the 64KB WASI limit", - "scripts": { - "asbuild:debug": "asc assembly/index.ts --target debug", - "asbuild:release": "asc assembly/index.ts --target release", - "asbuild": "npm run asbuild:debug && npm run asbuild:release" - }, - "dependencies": { - "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" - }, - "devDependencies": { - "@assemblyscript/wasi-shim": "^0.1.0", - "assemblyscript": "^0.28.9" - } -} -``` - - -### FILE: examples/largeDictionary/README.md - -``` -[← Back to examples](../README.md) - -# Large Dictionary - -This application demonstrates how to read large environment variables (> 64 KB) using the proxy-wasm dictionary API. - -## When to use `getDictionary` vs `getEnv` - -| Function | Use when | -|----------|----------| -| `getEnv(name)` | Variable value is under 64 KB (most cases) | -| `getDictionary(name)` | Variable value may exceed the 64 KB WASI env var size limit | - -The WASI environment variable interface has a **64 KB size limit** per variable. If your app needs to read larger values (e.g. large JSON configs, PEM certificates, policy documents), use `getDictionary` which calls `proxy_dictionary_get` and bypasses this limit. - -For all other environment variable access, prefer `getEnv` as it uses the standard WASI environment interface. - -## What it does - -In `onRequestHeaders`, the app reads the `LARGE_CONFIG` environment variable using `getDictionary`, logs its size, and adds it as an `x-config-size` request header for the upstream to see. - -## Configuration - -Set the following on your FastEdge application: - -| Name | Type | Description | -|------|------|-------------| -| `LARGE_CONFIG` | Environment variable | A large configuration payload (e.g. JSON, PEM certificate) | - -## Build - -```sh -pnpm install -pnpm run asbuild -``` - -Build output: - -| File | Description | -|------|-------------| -| `build/largeDictionary.wasm` | Optimised release binary — upload this to FastEdge | -| `build/largeDictionary-debug.wasm` | Debug binary with source maps | - -## Deploy - -Upload `build/largeDictionary.wasm` to the FastEdge portal and attach it to your CDN application. Configure the `LARGE_CONFIG` environment variable in the application settings. -``` diff --git a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/ab-testing-as.md b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/ab-testing-as.md index 2393c2e..306f924 100644 --- a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/ab-testing-as.md +++ b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/ab-testing-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -222,233 +222,3 @@ pnpm run asbuild - proxy-wasm-sdk-as assembly API reference - FastEdge environment variable configuration - FastEdge CDN application deployment guide - -## Source Material - -### FILE: examples/abTesting/assembly/index.ts - -```ts -export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; -import { - Context, - FilterHeadersStatusValues, - get_property, - log, - LogLevelValues, - registerRootContext, - RootContext, - send_http_response, - set_property, - stream_context, -} from "@gcoredev/proxy-wasm-sdk-as/assembly"; -import { - getEnv, - setLogLevel, -} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; -import { getCurrentTime } from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge/utils/runtime"; - -class AbTestingRoot extends RootContext { - createContext(context_id: u32): Context { - setLogLevel(LogLevelValues.info); - return new AbTestingContext(context_id, this); - } -} - -class AbTestingContext extends Context { - constructor(context_id: u32, root_context: AbTestingRoot) { - super(context_id, root_context); - } - - onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { - const experimentName = getEnv("EXPERIMENT_NAME"); - if (experimentName === "") { - send_http_response( - 500, - "internal server error", - String.UTF8.encode("App misconfigured - EXPERIMENT_NAME must be set"), - [], - ); - return FilterHeadersStatusValues.StopIteration; - } - - const variantAPath = getEnv("VARIANT_A_PATH"); - const variantBPath = getEnv("VARIANT_B_PATH"); - if (variantAPath === "" || variantBPath === "") { - send_http_response( - 500, - "internal server error", - String.UTF8.encode( - "App misconfigured - VARIANT_A_PATH and VARIANT_B_PATH must be set", - ), - [], - ); - return FilterHeadersStatusValues.StopIteration; - } - - // Check for existing experiment cookie - const cookieName = "fe_exp_" + experimentName; - const cookieHeader = stream_context.headers.request.get("Cookie"); - let assignedVariant = this.getCookieValue(cookieHeader, cookieName); - - // Assign variant if not already set - if (assignedVariant !== "A" && assignedVariant !== "B") { - // Use current time as a simple entropy source for 50/50 split - const now = getCurrentTime(); - assignedVariant = now % 2 == 0 ? "A" : "B"; - } - - // Rewrite the request path to the variant path - const pathArrBuf = get_property("request.path"); - if (pathArrBuf.byteLength === 0) { - return FilterHeadersStatusValues.Continue; - } - const originalPath = String.UTF8.decode(pathArrBuf); - const variantPath = assignedVariant === "A" ? variantAPath : variantBPath; - const newPath = variantPath + originalPath; - - // Reconstruct request.url from its decomposed parts rather than splicing - // the path out of the full URL — splicing breaks when the path happens to - // appear inside the host, and it can silently lose the query string. - const schemeBuf = get_property("request.scheme"); - const hostBuf = get_property("request.host"); - if (schemeBuf.byteLength > 0 && hostBuf.byteLength > 0) { - const scheme = String.UTF8.decode(schemeBuf); - const host = String.UTF8.decode(hostBuf); - const queryBuf = get_property("request.query"); - const query = queryBuf.byteLength > 0 ? String.UTF8.decode(queryBuf) : ""; - const newUrl = - scheme + "://" + host + newPath + (query.length > 0 ? "?" + query : ""); - log(LogLevelValues.info, `A/B routing: ${newUrl}`); - set_property("request.url", String.UTF8.encode(newUrl)); - } - - // Add variant header for upstream visibility - stream_context.headers.request.add("X-Experiment", experimentName); - stream_context.headers.request.add("X-Variant", assignedVariant); - - log( - LogLevelValues.info, - `A/B test "${experimentName}": variant ${assignedVariant}, path ${newPath}`, - ); - - return FilterHeadersStatusValues.Continue; - } - - onResponseHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { - // Recover the assigned variant from the request header set in onRequestHeaders. - // Instance state (this.variant) does not survive the nginx -> core-proxy hop. - const variant = stream_context.headers.request.get("X-Variant"); - if (variant === "") { - return FilterHeadersStatusValues.Continue; - } - - const experimentName = getEnv("EXPERIMENT_NAME"); - const cookieName = "fe_exp_" + experimentName; - - // Set the experiment cookie so subsequent requests stick to the same variant - stream_context.headers.response.add( - "Set-Cookie", - cookieName + "=" + variant + "; Path=/; Max-Age=86400; SameSite=Lax", - ); - - // Add variant as response header for observability - stream_context.headers.response.add("X-Variant", variant); - - return FilterHeadersStatusValues.Continue; - } - - private getCookieValue(cookieHeader: string, name: string): string { - if (cookieHeader === "") return ""; - const pairs = cookieHeader.split(";"); - const prefix = name + "="; - for (let i = 0; i < pairs.length; i++) { - const pair = pairs[i].trim(); - if (pair.startsWith(prefix)) { - return pair.substring(prefix.length); - } - } - return ""; - } -} - -registerRootContext((context_id: u32) => { - return new AbTestingRoot(context_id); -}, "abTesting"); -``` - - -### FILE: examples/abTesting/package.json - -```json -{ - "name": "fastedge-as-example-ab-testing", - "version": "1.0.0", - "description": "FastEdge AssemblyScript example: A/B Testing — cookie-based traffic splitting at the CDN layer", - "scripts": { - "asbuild:debug": "asc assembly/index.ts --target debug", - "asbuild:release": "asc assembly/index.ts --target release", - "asbuild": "npm run asbuild:debug && npm run asbuild:release" - }, - "dependencies": { - "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" - }, - "devDependencies": { - "@assemblyscript/wasi-shim": "^0.1.0", - "assemblyscript": "^0.28.9" - } -} -``` - - -### FILE: examples/abTesting/README.md - -``` -[← Back to examples](../README.md) - -# A/B Testing - -This application performs cookie-based A/B traffic splitting at the CDN layer, routing requests to different origin paths based on variant assignment. - -## What it does - -In `onRequestHeaders`, the app: - -1. Checks for an existing experiment cookie (`fe_exp_`). -2. If no cookie is found, assigns the user to variant **A** or **B** (50/50 split). -3. Rewrites the request path by prepending the variant-specific path prefix (e.g. `/variant-a/original/path`). -4. Adds `X-Experiment` and `X-Variant` request headers for upstream visibility. - -In `onResponseHeaders`, the app sets a `Set-Cookie` header to persist the variant assignment for subsequent requests (24-hour TTL). - -> **Note on variant assignment entropy:** New-visitor assignment uses `getCurrentTime() % 2` as a simple 50/50 source. This is illustrative — it is not sticky across two requests that arrive in the same millisecond and is not reproducible in tests. Production A/B implementations typically hash a stable visitor identifier (e.g. client IP or session token) for deterministic, sticky pre-cookie assignment. - -## Configuration - -Set the following environment variables on your FastEdge application: - -| Variable | Example | Description | -|----------|---------|-------------| -| `EXPERIMENT_NAME` | `homepage-redesign` | Name of the experiment (required) | -| `VARIANT_A_PATH` | `/variant-a` | Path prefix for variant A (required) | -| `VARIANT_B_PATH` | `/variant-b` | Path prefix for variant B (required) | - -Your origin server should serve different content at each variant path prefix. - -## Build - -```sh -pnpm install -pnpm run asbuild -``` - -Build output: - -| File | Description | -|------|-------------| -| `build/abTesting.wasm` | Optimised release binary — upload this to FastEdge | -| `build/abTesting-debug.wasm` | Debug binary with source maps | - -## Deploy - -Upload `build/abTesting.wasm` to the FastEdge portal and attach it to your CDN application. Configure the experiment environment variables in the application settings. -``` diff --git a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/auth-jwt-as.md b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/auth-jwt-as.md index 9da636d..a0f2b66 100644 --- a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/auth-jwt-as.md +++ b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/auth-jwt-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -261,6 +261,12 @@ pnpm install pnpm run asbuild ``` +## Deploy + +Upload `build/jwt.wasm` to the FastEdge portal and attach it to your CDN application. Configure the `SECRET` secret variable in the application settings. + +For more on secrets and secret rotation slots, see the FastEdge secrets documentation. + ## See Also - FastEdge secrets documentation (secret variables and rotation slots) diff --git a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/base-as.md b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/base-as.md index 6a43237..bc7b644 100644 --- a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/base-as.md +++ b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/base-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -14,8 +14,8 @@ languages: [assemblyscript] template_origin: cdn-base source_example: proxy-wasm-sdk-as/examples/helloWorld source_repo: proxy-wasm-sdk-as -source_ref: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 -updated: 2026-09-22 +source_ref: 073f583217448aaf25cb98f5d336a0fda26273ba +updated: 2026-09-29 --- # Base Skeleton: CDN AssemblyScript @@ -263,3 +263,118 @@ registerRootContext((context_id: u32) => { - platform-overview (CDN filter pipeline, hook execution order) - examples-headers-cdn-assemblyscript (header manipulation feature blueprint) - examples-body-cdn-assemblyscript (body transformation feature blueprint) + +## Source Material + +### FILE: examples/helloWorld/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; // this exports the required functions for the proxy to interact with us. +import { + Context, + FilterDataStatusValues, + FilterHeadersStatusValues, + log, + LogLevelValues, + registerRootContext, + RootContext, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; + +class HelloWorldRoot extends RootContext { + createContext(context_id: u32): Context { + return new HelloWorld(context_id, this); + } +} + +class HelloWorld extends Context { + constructor(context_id: u32, root_context: HelloWorldRoot) { + super(context_id, root_context); + } + + onRequestHeaders( + headers: u32, + end_of_stream: bool, + ): FilterHeadersStatusValues { + log(LogLevelValues.info, "onRequestHeaders >> Hello World!"); + return FilterHeadersStatusValues.Continue; + } + + onRequestBody( + body_buffer_length: usize, + end_of_stream: bool, + ): FilterDataStatusValues { + log(LogLevelValues.info, "onRequestBody >> Hello World!"); + return FilterDataStatusValues.Continue; + } + + onResponseHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + log(LogLevelValues.info, "onResponseHeaders >> Hello World!"); + return FilterHeadersStatusValues.Continue; + } + + onResponseBody( + body_buffer_length: usize, + end_of_stream: bool, + ): FilterDataStatusValues { + log(LogLevelValues.info, "onResponseBody >> Hello World!"); + return FilterDataStatusValues.Continue; + } +} + +registerRootContext((context_id: u32) => { + return new HelloWorldRoot(context_id); +}, "helloWorld"); +``` + + +### FILE: examples/helloWorld/package.json + +```json +{ + "name": "fastedge-as-example-hello-world", + "version": "1.0.0", + "description": "FastEdge AssemblyScript example: Hello World — minimal CDN app skeleton", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + + +### FILE: examples/helloWorld/asconfig.json + +```json +{ + "extends": "./node_modules/@assemblyscript/wasi-shim/asconfig.json", + "targets": { + "debug": { + "outFile": "build/helloWorld-debug.wasm", + "textFile": "build/helloWorld-debug.wat", + "sourceMap": true, + "debug": true + }, + "release": { + "outFile": "build/helloWorld.wasm", + "textFile": "build/helloWorld.wat", + "sourceMap": true, + "optimizeLevel": 3, + "shrinkLevel": 0, + "converge": false, + "noAssert": false + } + }, + "options": { + "bindings": "esm", + "use": "abort=abort_proc_exit" + } +} +``` diff --git a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/body-as.md b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/body-as.md index 89784fd..6daeb0a 100644 --- a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/body-as.md +++ b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/body-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -352,7 +352,7 @@ Build scripts defined in `package.json`: ## Deploy -Upload `build/body.wasm` via the FastEdge portal and attach it to your CDN application. +Upload `build/body.wasm` to the FastEdge portal and attach it to your CDN application. ## See Also @@ -360,218 +360,3 @@ Upload `build/body.wasm` via the FastEdge portal and attach it to your CDN appli - cdn-base skeleton - FastEdge portal deployment guide - fastedge-test reference (for testing body manipulation logic) - -## Source Material - -### FILE: examples/body/assembly/index.ts - -```ts -export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; // this exports the required functions for the proxy to interact with us. -import { - BufferTypeValues, - Context, - FilterDataStatusValues, - FilterHeadersStatusValues, - get_buffer_bytes, - get_property, - log, - LogLevelValues, - registerRootContext, - RootContext, - set_buffer_bytes, - set_property, - stream_context, -} from "@gcoredev/proxy-wasm-sdk-as/assembly"; -import { setLogLevel } from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; - -class HttpBodyRoot extends RootContext { - createContext(context_id: u32): Context { - setLogLevel(LogLevelValues.info); // Set the log level to info - for more logging reduce this to LogLevelValues.debug - return new HttpBody(context_id, this); - } -} - -class HttpBody extends Context { - constructor(context_id: u32, root_context: HttpBodyRoot) { - super(context_id, root_context); - } - - onRequestHeaders( - headers: u32, - end_of_stream: bool - ): FilterHeadersStatusValues { - log(LogLevelValues.debug, "onRequestHeaders >>"); - // Remove the "content-length" header - stream_context.headers.request.remove("content-length"); - return FilterHeadersStatusValues.Continue; - } - - onRequestBody( - body_buffer_length: usize, - end_of_stream: bool - ): FilterDataStatusValues { - log(LogLevelValues.debug, "onRequestBody >>"); - if (!end_of_stream) { - // Wait until the complete body is buffered - return FilterDataStatusValues.StopIterationAndBuffer; - } - - // Retrieve the body from the HttpRequestBody buffer - const bodyBytes = get_buffer_bytes( - BufferTypeValues.HttpRequestBody, - 0, - body_buffer_length - ); - - if (bodyBytes.byteLength > 0) { - const bodyStr = String.UTF8.decode(bodyBytes); - log(LogLevelValues.debug, "onRequestBody >> bodyStr: " + bodyStr); - if (bodyStr.includes("Client")) { - const newBody = `Original message body (${body_buffer_length.toString()} bytes) redacted.\n`; - set_buffer_bytes( - BufferTypeValues.HttpRequestBody, - 0, - body_buffer_length, - String.UTF8.encode(newBody) - ); - } - } - return FilterDataStatusValues.Continue; - } - - onResponseHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { - log(LogLevelValues.debug, "onResponseHeaders >>"); - - // Remove "content-length" header as the body size will change - stream_context.headers.response.remove("content-length"); - - // Set "transfer-encoding" to "chunked" - stream_context.headers.response.replace("transfer-encoding", "Chunked"); - - const contentType = stream_context.headers.response.get("content-type"); - if (contentType.length > 0) { - set_property("response.content_type", String.UTF8.encode(contentType)); - } - - return FilterHeadersStatusValues.Continue; - } - - onResponseBody( - body_buffer_length: usize, - end_of_stream: bool - ): FilterDataStatusValues { - log(LogLevelValues.debug, "onResponseBody >>" + end_of_stream.toString()); - - if (!end_of_stream) { - // Wait until the complete body is buffered - return FilterDataStatusValues.StopIterationAndBuffer; - } - - log( - LogLevelValues.debug, - "onResponseBody >> body_buffer_length: " + body_buffer_length.toString() - ); - - // Retrieve the request URL - const urlBytes = get_property("request.url"); - const url = urlBytes.byteLength === 0 ? "" : String.UTF8.decode(urlBytes); - if (url !== "") { - log(LogLevelValues.info, `url=${url}`); - } - - // Retrieve the response content type stored in onResponseHeaders - const contentTypeBytes = get_property("response.content_type"); - const contentType = - contentTypeBytes.byteLength === 0 - ? "" - : String.UTF8.decode(contentTypeBytes); - if (contentType !== "") { - log(LogLevelValues.info, `contentType=${contentType}`); - } - - // Retrieve the body from the HttpRequestBody buffer - const bodyBytes = get_buffer_bytes( - BufferTypeValues.HttpResponseBody, - 0, - body_buffer_length - ); - - if (bodyBytes.byteLength > 0) { - const bodyStr = String.UTF8.decode(bodyBytes); - log(LogLevelValues.info, "onResponseBody >> bodyStr: " + bodyStr); - } - return FilterDataStatusValues.Continue; - } -} - -registerRootContext((context_id: u32) => { - return new HttpBodyRoot(context_id); -}, "httpbody"); -``` - - -### FILE: examples/body/package.json - -```json -{ - "name": "fastedge-as-example-body", - "version": "1.0.0", - "description": "FastEdge AssemblyScript example: Body manipulation", - "scripts": { - "asbuild:debug": "asc assembly/index.ts --target debug", - "asbuild:release": "asc assembly/index.ts --target release", - "asbuild": "npm run asbuild:debug && npm run asbuild:release" - }, - "dependencies": { - "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" - }, - "devDependencies": { - "@assemblyscript/wasi-shim": "^0.1.0", - "assemblyscript": "^0.28.9" - } -} -``` - - -### FILE: examples/body/README.md - -``` -[← Back to examples](../README.md) - -# Body - -This application modifies the request and response body using the `onRequestBody` and `onResponseBody` lifecycle hooks. - -## What it does - -1. **`onRequestHeaders`** — removes the `content-length` header, required because the body content will be altered. - -2. **`onRequestBody`** — buffers the full request body then checks whether it contains the word `Client`. If found, the body is replaced with a redaction notice. - -3. **`onResponseHeaders`** — removes `content-length`, sets `transfer-encoding: Chunked`, and captures the `content-type` into a runtime property. - -4. **`onResponseBody`** — logs the request URL, content type, and full response body once the stream is complete. - -This demonstrates the basic flow for body manipulation across all lifecycle hooks. Key points: - -- Headers must be adjusted _before_ modifying the body (`content-length`, `transfer-encoding`). -- The `end_of_stream` flag is checked before processing, allowing the body to be buffered across multiple invocations before acting on it. - -## Build - -```sh -pnpm install -pnpm run asbuild -``` - -Build output: - -| File | Description | -| ----------------------- | -------------------------------------------------- | -| `build/body.wasm` | Optimised release binary — upload this to FastEdge | -| `build/body-debug.wasm` | Debug binary with source maps | - -## Deploy - -Upload `build/body.wasm` to the FastEdge portal and attach it to your CDN application. -``` diff --git a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/custom-error-pages-as.md b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/custom-error-pages-as.md index bb0b1ac..2403635 100644 --- a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/custom-error-pages-as.md +++ b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/custom-error-pages-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 - updated: 2026-08-20 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -324,3 +324,241 @@ Upload `build/customErrorPages.wasm` to the FastEdge portal and attach it to you - CDN base skeleton reference - host-services-rust reference (for Rust equivalent patterns) - FastEdge platform overview + +## Source Material + +### FILE: examples/customErrorPages/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; +import { + Context, + FilterDataStatusValues, + FilterHeadersStatusValues, + get_property, + log, + LogLevelValues, + registerRootContext, + RootContext, + set_buffer_bytes, + BufferTypeValues, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { setLogLevel } from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; + +class ErrorPagesRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); + return new ErrorPagesContext(context_id, this); + } +} + +class ErrorPagesContext extends Context { + constructor(context_id: u32, root_context: ErrorPagesRoot) { + super(context_id, root_context); + } + + onResponseHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + const statusBuf = get_property("response.status"); + if (statusBuf.byteLength < 2) { + return FilterHeadersStatusValues.Continue; + } + + const bytes = Uint8Array.wrap(statusBuf); + const code: u32 = (u32(bytes[0]) << 8) | u32(bytes[1]); + + if (code >= 400 && code < 600) { + stream_context.headers.response.replace("Content-Type", "text/html"); + stream_context.headers.response.remove("Content-Length"); + stream_context.headers.response.replace("Transfer-Encoding", "Chunked"); + + log(LogLevelValues.info, "Error response detected: " + code.toString()); + } + + return FilterHeadersStatusValues.Continue; + } + + onResponseBody( + body_buffer_length: usize, + end_of_stream: bool, + ): FilterDataStatusValues { + // Read response status from property (no instance state between hooks) + const statusBuf = get_property("response.status"); + if (statusBuf.byteLength < 2) { + return FilterDataStatusValues.Continue; + } + + const bytes = Uint8Array.wrap(statusBuf); + const code: u32 = (u32(bytes[0]) << 8) | u32(bytes[1]); + + if (code < 400 || code >= 600) { + return FilterDataStatusValues.Continue; + } + + if (!end_of_stream) { + return FilterDataStatusValues.StopIterationAndBuffer; + } + const title = this.getErrorTitle(code); + const description = this.getErrorDescription(code); + const category = code >= 500 ? "Server Error" : "Client Error"; + + const html = + '' + + '' + + "" + + code.toString() + + " — " + + title + + "" + + "
" + + "

" + + code.toString() + + "

" + + "

" + + title + + "

" + + "

" + + description + + "

" + + "

" + + category + + "

" + + "
"; + + const body = String.UTF8.encode(html); + // Replace the entire original body — pass body_buffer_length as the length + // to replace, not body.byteLength. Otherwise any original bytes beyond the + // new body's length survive at the tail of the response. + set_buffer_bytes( + BufferTypeValues.HttpResponseBody, + 0, + body_buffer_length as u32, + body, + ); + + return FilterDataStatusValues.Continue; + } + + private getErrorTitle(code: u32): string { + if (code == 400) return "Bad Request"; + if (code == 401) return "Unauthorized"; + if (code == 403) return "Forbidden"; + if (code == 404) return "Not Found"; + if (code == 405) return "Method Not Allowed"; + if (code == 408) return "Request Timeout"; + if (code == 429) return "Too Many Requests"; + if (code == 500) return "Internal Server Error"; + if (code == 502) return "Bad Gateway"; + if (code == 503) return "Service Unavailable"; + if (code == 504) return "Gateway Timeout"; + if (code >= 500) return "Server Error"; + return "Error"; + } + + private getErrorDescription(code: u32): string { + if (code == 400) + return "The server could not understand the request due to invalid syntax."; + if (code == 401) + return "You need to authenticate to access this resource."; + if (code == 403) + return "You do not have permission to access this resource."; + if (code == 404) + return "The requested page could not be found. It may have been moved or deleted."; + if (code == 405) + return "The request method is not supported for this resource."; + if (code == 408) + return "The server timed out waiting for the request."; + if (code == 429) + return "You have sent too many requests. Please try again later."; + if (code == 500) + return "The server encountered an unexpected condition that prevented it from fulfilling the request."; + if (code == 502) + return "The server received an invalid response from the upstream server."; + if (code == 503) + return "The server is temporarily unavailable. Please try again later."; + if (code == 504) + return "The server did not receive a timely response from the upstream server."; + if (code >= 500) + return "The server encountered an error processing your request."; + return "An error occurred processing your request."; + } +} + +registerRootContext((context_id: u32) => { + return new ErrorPagesRoot(context_id); +}, "customErrorPages"); +``` + + +### FILE: examples/customErrorPages/package.json + +```json +{ + "name": "fastedge-as-example-custom-error-pages", + "version": "1.0.0", + "description": "FastEdge AssemblyScript example: Custom Error Pages — replace 4xx/5xx responses with branded HTML", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + + +### FILE: examples/customErrorPages/README.md + +``` +[← Back to examples](../README.md) + +# Custom Error Pages + +This application intercepts 4xx and 5xx error responses and replaces them with clean, branded HTML error pages. + +## What it does + +In `onResponseHeaders`, the app reads the `response.status` property. If it is in the 400-599 range, it sets the `Content-Type` to `text/html` and prepares for body replacement. + +In `onResponseBody`, the app buffers the full response, then replaces the body with a styled HTML page containing: + +- The numeric status code +- A human-readable error title (e.g. "Not Found", "Bad Gateway") +- A description explaining what went wrong +- An error category label ("Client Error" or "Server Error") + +Covers all common HTTP error codes (400, 401, 403, 404, 405, 408, 429, 500, 502, 503, 504) with specific messages and falls back to generic messages for other codes. + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +|------|-------------| +| `build/customErrorPages.wasm` | Optimised release binary — upload this to FastEdge | +| `build/customErrorPages-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/customErrorPages.wasm` to the FastEdge portal and attach it to your CDN application. No environment variables or secrets are required. +``` diff --git a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/env-secrets-as.md b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/env-secrets-as.md index 0a75003..8b32b7e 100644 --- a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/env-secrets-as.md +++ b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/env-secrets-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: 8e3bb621bc013a0aed7e52122066b417ad62a207 - updated: 2026-08-20 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -179,3 +179,140 @@ Upload `build/variablesAndSecrets.wasm` to the FastEdge portal and attach it to - cdn-base skeleton (base request/response handling structure) - fastedge-test reference (local WASM test runner and fixture format) - platform-overview reference (FastEdge application configuration and secret management) + +## Source Material + +### FILE: examples/variablesAndSecrets/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; // this exports the required functions for the proxy to interact with us. +import { + Context, + FilterHeadersStatusValues, + log, + LogLevelValues, + registerRootContext, + RootContext, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { + getEnv, + getSecret, + setLogLevel, +} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; + +class VariablesRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); + return new VariablesContext(context_id, this); + } +} + +class VariablesContext extends Context { + constructor(context_id: u32, root_context: VariablesRoot) { + super(context_id, root_context); + } + + onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + const username = getEnv("USERNAME"); + const password = getSecret("PASSWORD"); + + log(LogLevelValues.info, "USERNAME: " + username); + log(LogLevelValues.info, "PASSWORD: [set, length " + password.length.toString() + "]"); + + stream_context.headers.request.add("x-env-username", username); + stream_context.headers.request.add("x-env-password", password); + + return FilterHeadersStatusValues.Continue; + } +} + +registerRootContext((context_id: u32) => { + return new VariablesRoot(context_id); +}, "variablesAndSecrets"); +``` + + +### FILE: examples/variablesAndSecrets/package.json + +```json +{ + "name": "fastedge-as-example-variables-and-secrets", + "version": "0.0.1", + "description": "FastEdge AssemblyScript example: Variables and Secrets", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + + +### FILE: examples/variablesAndSecrets/README.md + +``` +[← Back to examples](../README.md) + +# Variables and Secrets + +This application demonstrates reading environment variables and secrets, then forwarding their values as request headers to the upstream. + +## What it does + +In `onRequestHeaders`, the app: + +1. Reads the `USERNAME` environment variable using `getEnv`. +2. Reads the `PASSWORD` secret using `getSecret`. +3. Logs that both values were retrieved (without logging the secret value itself). +4. Injects them as `x-env-username` and `x-env-password` request headers so the upstream receives them. + +This is useful as a reference for understanding how to access environment variables and secrets within a FastEdge plugin. + +> **Security warning:** Never log secret values verbatim in production. Logs are often persisted and accessible to operators who should not see credential values. This example logs the secret's length rather than its content. Similarly, be deliberate about which upstream systems receive secret values via forwarded headers — limit forwarding to systems that need it. + +## Configuration + +Set the following on your FastEdge application: + +| Name | Type | Description | +| ---------- | -------------------- | -------------------------------------- | +| `USERNAME` | Environment variable | The username value to forward upstream | +| `PASSWORD` | Secret | The password value to forward upstream | + +## Local testing + +The fixture at `fixtures/happy-path.test.json` uses `"dotenv": {"enabled": true}` to load values from `fixtures/.env`. The runner maps `FASTEDGE_VAR_ENV_` to `getEnv("NAME")` and `FASTEDGE_VAR_SECRET_` to `getSecret("NAME")`. + +To test locally with the visual debugger, create `fixtures/.env`: + +``` +FASTEDGE_VAR_ENV_USERNAME=my-username +FASTEDGE_VAR_SECRET_PASSWORD=my-password +``` + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +| -------------------------------------- | -------------------------------------------------- | +| `build/variablesAndSecrets.wasm` | Optimised release binary — upload this to FastEdge | +| `build/variablesAndSecrets-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/variablesAndSecrets.wasm` to the FastEdge portal and attach it to your CDN application. Configure the `USERNAME` environment variable and the `PASSWORD` secret in the application settings. +``` diff --git a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/geo-redirect-as.md b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/geo-redirect-as.md index 35f839a..2fc9227 100644 --- a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/geo-redirect-as.md +++ b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/geo-redirect-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -266,185 +266,3 @@ Build outputs: - platform-overview (runtime properties, Geo-IP data) - deploy skill reference - manage skill reference (environment variable configuration) - -## Source Material - -### FILE: examples/geoRedirect/assembly/index.ts - -```ts -export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; // this exports the required functions for the proxy to interact with us. -import { - Context, - FilterHeadersStatusValues, - get_property, - log, - LogLevelValues, - registerRootContext, - RootContext, - send_http_response, - set_property, -} from "@gcoredev/proxy-wasm-sdk-as/assembly"; -import { - getEnv, - setLogLevel, -} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; - -const BAD_GATEWAY: u32 = 502; -const INTERNAL_SERVER_ERROR: u32 = 500; - -class GeoRedirectRoot extends RootContext { - createContext(context_id: u32): Context { - setLogLevel(LogLevelValues.info); - return new GeoRedirect(context_id, this); - } -} - -class GeoRedirect extends Context { - constructor(context_id: u32, root_context: GeoRedirectRoot) { - super(context_id, root_context); - } - - onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { - log(LogLevelValues.info, "onRequestHeaders >> "); - - const defaultOrigin = getEnv("DEFAULT"); - - if (!defaultOrigin) { - send_http_response( - INTERNAL_SERVER_ERROR, - "internal server error", - String.UTF8.encode("App misconfigured - DEFAULT must be set"), - [], - ); - return FilterHeadersStatusValues.StopIteration; - } - - const countryArrBuf = get_property("request.country"); - if (countryArrBuf.byteLength === 0) { - send_http_response( - BAD_GATEWAY, - "bad gateway", - String.UTF8.encode("Missing country information"), - [], - ); - return FilterHeadersStatusValues.StopIteration; - } - const countryCode = String.UTF8.decode(countryArrBuf); - const countrySpecificOrigin = getEnv(countryCode); - - log( - LogLevelValues.info, - `Country code: ( ${countryCode} ): ${ - countrySpecificOrigin === "" ? "no matching origin" : countrySpecificOrigin - }`, - ); - - const hostArrBuf = get_property("request.host"); - if (hostArrBuf.byteLength > 0) { - const host = String.UTF8.decode(hostArrBuf); - log(LogLevelValues.info, `Provided Host: ${host}`); - } - - const pathArrBuf = get_property("request.path"); - if (pathArrBuf.byteLength === 0) { - send_http_response( - INTERNAL_SERVER_ERROR, - "internal server error", - String.UTF8.encode("Internal server error - no request path"), - [], - ); - return FilterHeadersStatusValues.StopIteration; - } - - const path = String.UTF8.decode(pathArrBuf); - const origin = countrySpecificOrigin === "" ? defaultOrigin : countrySpecificOrigin; - // remove trailing slashes from the origin - const cleanedOrigin = origin.endsWith("/") ? origin.slice(0, -1) : origin; - - const requestUrl = `${cleanedOrigin}${path}`; - - log(LogLevelValues.info, `request-url: ${requestUrl}`); - - set_property("request.url", String.UTF8.encode(requestUrl)); - - return FilterHeadersStatusValues.Continue; - } -} - -registerRootContext((context_id: u32) => { - return new GeoRedirectRoot(context_id); -}, "geoRedirect"); -``` - - -### FILE: examples/geoRedirect/package.json - -```json -{ - "name": "fastedge-as-example-georedirect", - "version": "1.0.0", - "description": "FastEdge AssemblyScript example: Geo Redirect", - "scripts": { - "asbuild:debug": "asc assembly/index.ts --target debug", - "asbuild:release": "asc assembly/index.ts --target release", - "asbuild": "npm run asbuild:debug && npm run asbuild:release" - }, - "dependencies": { - "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" - }, - "devDependencies": { - "@assemblyscript/wasi-shim": "^0.1.0", - "assemblyscript": "^0.28.9" - } -} -``` - - -### FILE: examples/geoRedirect/README.md - -``` -[← Back to examples](../README.md) - -# Geo Redirect - -This application redirects requests to different origin URLs based on the client's country code. - -## What it does - -In `onRequestHeaders`, the app reads the client's country code from the `request.country` runtime property (populated by FastEdge's Geo-IP data) and looks up a matching environment variable by that country code. The `request.url` runtime property is then set to route the upstream fetch to the corresponding origin. - -- If a country-specific origin is configured (e.g. env var `DE=https://de.example.com`), the request is routed there. -- Otherwise it falls back to the `DEFAULT` origin. -- Uses [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country codes. - -> **Routing mechanism:** This is not an HTTP redirect (no `Location` header, no 302 response). Setting `request.url` rewrites the upstream fetch target transparently — the client sees a normal 200 response from the matched origin. - -> **Observability:** The app logs the country code, matched origin, and final request URL at INFO level, visible in the FastEdge application logs. - -## Configuration - -Set the following environment variables on your FastEdge application: - -| Variable | Example | Description | -|----------|---------|-------------| -| `DEFAULT` | `https://origin.example.com` | Fallback origin URL (required) | -| `` | `DE=https://de.example.com` | Per-country origin URL (optional, one per country) | - -## Build - -```sh -pnpm install -pnpm run asbuild -``` - -Build output: - -| File | Description | -|------|-------------| -| `build/geoRedirect.wasm` | Optimised release binary — upload this to FastEdge | -| `build/geoRedirect-debug.wasm` | Debug binary with source maps | - -## Deploy - -Upload `build/geoRedirect.wasm` to the FastEdge portal and attach it to your CDN application. Configure the `DEFAULT` environment variable and any per-country overrides in the application settings. -``` diff --git a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/large-dictionary-as.md b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/large-dictionary-as.md index bb51254..6127ba8 100644 --- a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/large-dictionary-as.md +++ b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/large-dictionary-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -138,3 +138,129 @@ No additional dependencies beyond the cdn-base skeleton. - cdn-base skeleton reference - proxy-wasm-sdk-as SDK reference (AssemblyScript) - platform-overview reference for environment variable configuration + +## Source Material + +### FILE: examples/largeDictionary/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; +import { + Context, + FilterHeadersStatusValues, + log, + LogLevelValues, + registerRootContext, + RootContext, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { + getDictionary, + setLogLevel, +} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; + +class LargeDictionaryRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); + return new LargeDictionaryContext(context_id, this); + } +} + +class LargeDictionaryContext extends Context { + constructor(context_id: u32, root_context: LargeDictionaryRoot) { + super(context_id, root_context); + } + + onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + // Use getDictionary for environment variables that may exceed 64KB. + // For normal-sized env vars (< 64KB), use getEnv instead. + const config = getDictionary("LARGE_CONFIG"); + + const size = config.length; + log(LogLevelValues.info, "LARGE_CONFIG size: " + size.toString() + " bytes"); + + stream_context.headers.request.add("x-config-size", size.toString()); + + return FilterHeadersStatusValues.Continue; + } +} + +registerRootContext((context_id: u32) => { + return new LargeDictionaryRoot(context_id); +}, "largeDictionary"); +``` + + +### FILE: examples/largeDictionary/package.json + +```json +{ + "name": "fastedge-as-example-large-dictionary", + "version": "1.0.0", + "description": "FastEdge AssemblyScript example: Large Dictionary — read env vars exceeding the 64KB WASI limit", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + + +### FILE: examples/largeDictionary/README.md + +``` +[← Back to examples](../README.md) + +# Large Dictionary + +This application demonstrates how to read large environment variables (> 64 KB) using the proxy-wasm dictionary API. + +## When to use `getDictionary` vs `getEnv` + +| Function | Use when | +|----------|----------| +| `getEnv(name)` | Variable value is under 64 KB (most cases) | +| `getDictionary(name)` | Variable value may exceed the 64 KB WASI env var size limit | + +The WASI environment variable interface has a **64 KB size limit** per variable. If your app needs to read larger values (e.g. large JSON configs, PEM certificates, policy documents), use `getDictionary` which calls `proxy_dictionary_get` and bypasses this limit. + +For all other environment variable access, prefer `getEnv` as it uses the standard WASI environment interface. + +## What it does + +In `onRequestHeaders`, the app reads the `LARGE_CONFIG` environment variable using `getDictionary`, logs its size, and adds it as an `x-config-size` request header for the upstream to see. + +## Configuration + +Set the following on your FastEdge application: + +| Name | Type | Description | +|------|------|-------------| +| `LARGE_CONFIG` | Environment variable | A large configuration payload (e.g. JSON, PEM certificate) | + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +|------|-------------| +| `build/largeDictionary.wasm` | Optimised release binary — upload this to FastEdge | +| `build/largeDictionary-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/largeDictionary.wasm` to the FastEdge portal and attach it to your CDN application. Configure the `LARGE_CONFIG` environment variable in the application settings. +``` diff --git a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/properties-as.md b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/properties-as.md index 416db62..f83b7e1 100644 --- a/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/properties-as.md +++ b/plugins/gcore-fastedge-cursor/skills/scaffold/reference/cdn/properties-as.md @@ -2,9 +2,9 @@ auto-updated: true sources: - id: proxy-wasm-sdk-as - ref: master - commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 - updated: 2026-09-22 + ref: v1.2.4 + commit: 073f583217448aaf25cb98f5d336a0fda26273ba + updated: 2026-09-29 --> --- @@ -529,3 +529,270 @@ Upload `build/properties.wasm` to the FastEdge portal and attach it to your CDN - platform-overview (CDN app request lifecycle, available runtime properties) - host-services reference (get_property, set_property, send_http_response, log) - CDN Properties list (full list of available runtime property paths) + +## Source Material + +### FILE: examples/properties/assembly/index.ts + +```ts +export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; // this exports the required functions for the proxy to interact with us. +import { + Context, + FilterHeadersStatusValues, + get_property, + log, + LogLevelValues, + registerRootContext, + RootContext, + send_http_response, + set_property, + stream_context, +} from "@gcoredev/proxy-wasm-sdk-as/assembly"; +import { + setLogLevel, +} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge"; + +const REQUEST_URI = "request.url"; +const REQUEST_HOST = "request.host"; +const REQUEST_PATH = "request.path"; +const REQUEST_SCHEME = "request.scheme"; +const REQUEST_EXTENSION = "request.extension"; +const REQUEST_QUERY = "request.query"; +const REQUEST_X_REAL_IP = "request.x_real_ip"; +const REQUEST_COUNTRY = "request.country"; +const REQUEST_CITY = "request.city"; + +class PropertiesRoot extends RootContext { + createContext(context_id: u32): Context { + setLogLevel(LogLevelValues.info); + return new Properties(context_id, this); + } +} + +class Properties extends Context { + constructor(context_id: u32, root_context: PropertiesRoot) { + super(context_id, root_context); + } + + onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { + // Error codes 551–559 identify the absent property via the HTTP response status: + // 551=uri 552=host 553=path 554=scheme 555=extension 556=query 557=x_real_ip 558=country 559=city + if (!this.handleProperty(REQUEST_URI, 551, "uri", "request-uri")) { + return FilterHeadersStatusValues.StopIteration; + } + + // host must be present for upstream routing; validated but not logged or exposed as a response header + if (!this.handleProperty(REQUEST_HOST, 552)) { + return FilterHeadersStatusValues.StopIteration; + } + + if (!this.handleProperty(REQUEST_PATH, 553, "path", "request-path")) { + return FilterHeadersStatusValues.StopIteration; + } + + if (!this.handleProperty(REQUEST_SCHEME, 554, "scheme", "request-scheme")) { + return FilterHeadersStatusValues.StopIteration; + } + + if (!this.handleProperty(REQUEST_EXTENSION, 555, "extension", "request-extension", true)) { + return FilterHeadersStatusValues.StopIteration; + } + + if (!this.handleProperty(REQUEST_QUERY, 556, "query", "request-query", true)) { + return FilterHeadersStatusValues.StopIteration; + } + + if (!this.handleProperty(REQUEST_X_REAL_IP, 557, "client_ip", "request-x-real-ip")) { + return FilterHeadersStatusValues.StopIteration; + } + + if (!this.handleProperty(REQUEST_COUNTRY, 558, "country", "request-country")) { + return FilterHeadersStatusValues.StopIteration; + } + + if (!this.handleProperty(REQUEST_CITY, 559, "city", "request-city")) { + return FilterHeadersStatusValues.StopIteration; + } + + // Handle query parameters + const query = get_property(REQUEST_QUERY); + if (query.byteLength !== 0) { + const queryString = String.UTF8.decode(query); + log(LogLevelValues.info, "query=" + queryString); + const params = queryString + .split("&") + .map>((pair) => pair.split("=")); + + for (let i = 0; i < params.length; i++) { + const param = params[i]; + if (param.length !== 2) { + continue; // Skip invalid query parameters + } + const key = param[0]; + const value = param[1]; + if (key.toLowerCase() === "url") { + log(LogLevelValues.info, `change url to: ${value}`); + set_property(REQUEST_URI, String.UTF8.encode(value)); + } else if (key.toLowerCase() === "host") { + log(LogLevelValues.info, `change host to: ${value}`); + set_property(REQUEST_HOST, String.UTF8.encode(value)); + } else if (key.toLowerCase() === "path") { + log(LogLevelValues.info, `change path to: ${value}`); + set_property(REQUEST_PATH, String.UTF8.encode(value)); + } + } + } + + return FilterHeadersStatusValues.Continue; + } + + // allowEmpty=true for properties that may legitimately be absent (e.g. request.extension + // when the path has no file extension, request.query when there is no query string). + // In that case the property is logged with an empty value and processing continues + // without adding an empty response header. + private handleProperty( + propertyKey: string, + errorCode: u32, + propertyName: string = "", + headerName: string = "", + allowEmpty: boolean = false + ): boolean { + const valueArr = get_property(propertyKey); + if (valueArr.byteLength === 0) { + if (allowEmpty) { + if (propertyName.length > 0) { + log(LogLevelValues.info, "onRequestHeaders >> " + propertyName + ": "); + } + return true; + } + send_http_response( + errorCode, + "internal server error", + String.UTF8.encode("Internal server error"), + [] + ); + return false; + } + const value = String.UTF8.decode(valueArr); + if (propertyName.length > 0) { + log(LogLevelValues.info, "onRequestHeaders >> " + propertyName + ": " + value); + } + if (headerName.length > 0) { + stream_context.headers.response.add(headerName, value); + } + return true; + } +} + +registerRootContext((context_id: u32) => { + return new PropertiesRoot(context_id); +}, "properties"); +``` + + +### FILE: examples/properties/package.json + +```json +{ + "name": "fastedge-as-example-properties", + "version": "1.0.0", + "description": "FastEdge AssemblyScript example: Runtime properties", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release" + }, + "dependencies": { + "@gcoredev/proxy-wasm-sdk-as": "^1.2.3" + }, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.28.9" + } +} +``` + + +### FILE: examples/properties/README.md + +``` +[← Back to examples](../README.md) + +# Properties + +This application reads and logs all known FastEdge runtime properties, exposing them as response headers. + +> Note: This example does not cover all the available properties See the [CDN Properties](https://gcore.com/docs/fastedge/getting-started/cdn-properties) list. + +## What it does + +In `onRequestHeaders`, the app reads the following runtime properties using `get_property()` and adds each as a response header: + +| Property | Response header | +| ------------------- | ---------------------- | +| `request.url` | `request-uri` | +| `request.host` | *(validated only)* | +| `request.path` | `request-path` | +| `request.scheme` | `request-scheme` | +| `request.extension` | `request-extension` | +| `request.query` | `request-query` | +| `request.x_real_ip` | `request-x-real-ip` | +| `request.country` | `request-country` | +| `request.city` | `request-city` | + +`request.host` is validated for presence but not logged or exposed as a header — it must be non-empty for the upstream request to route correctly. + +`request.extension` and `request.query` are optional: if absent, the property is logged with an empty value and processing continues without adding a response header. + +If a required property is absent, the app returns a numeric HTTP status identifying the gap: + +| Status | Property | +|--------|----------| +| 551 | `request.url` | +| 552 | `request.host` | +| 553 | `request.path` | +| 554 | `request.scheme` | +| 555 | `request.extension` *(optional — never triggered)* | +| 556 | `request.query` *(optional — never triggered)* | +| 557 | `request.x_real_ip` | +| 558 | `request.country` | +| 559 | `request.city` | + +The app also supports overriding `request.url`, `request.host`, and `request.path` via query parameters (`url=`, `host=`, `path=`). + +## Expected output + +For a request to `/page.html?test=value` with geo properties populated, the app logs at INFO level: + +``` +onRequestHeaders >> uri: https://example.com/page.html?test=value +onRequestHeaders >> path: /page.html?test=value +onRequestHeaders >> scheme: https +onRequestHeaders >> extension: html +onRequestHeaders >> query: test=value +onRequestHeaders >> client_ip: 203.0.113.1 +onRequestHeaders >> country: LU +onRequestHeaders >> city: Luxembourg +query=test=value +``` + +The corresponding `request-*` response headers are also set. Logs are visible in the FastEdge application logs. + +## Build + +```sh +pnpm install +pnpm run asbuild +``` + +Build output: + +| File | Description | +| ----------------------------- | -------------------------------------------------- | +| `build/properties.wasm` | Optimised release binary — upload this to FastEdge | +| `build/properties-debug.wasm` | Debug binary with source maps | + +## Deploy + +Upload `build/properties.wasm` to the [FastEdge portal](https://portal.gcore.com) and attach it to your CDN application. No environment variables are required. +``` diff --git a/plugins/gcore-fastedge/.claude-plugin/plugin.json b/plugins/gcore-fastedge/.claude-plugin/plugin.json index 67ce176..a7addd8 100644 --- a/plugins/gcore-fastedge/.claude-plugin/plugin.json +++ b/plugins/gcore-fastedge/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "gcore-fastedge", - "version": "0.2.11", + "version": "0.2.12", "description": "Build, deploy, and manage serverless edge applications on Gcore FastEdge — Wasm-powered compute on 210+ global PoPs", "author": { "name": "Gcore", diff --git a/plugins/gcore-fastedge/docs-index.json b/plugins/gcore-fastedge/docs-index.json index 338f770..6e73c5e 100644 --- a/plugins/gcore-fastedge/docs-index.json +++ b/plugins/gcore-fastedge/docs-index.json @@ -1,9 +1,9 @@ { "schema_version": "1.0.0", - "generated_at": "2026-09-24T09:24:50.621Z", + "generated_at": "2026-10-02T09:07:59.419Z", "source": { "repo": "fastedge-plugin", - "commit": "503a8fc", + "commit": "0db9306", "pipeline": "sync-reference-docs" }, "topics": [ @@ -528,7 +528,7 @@ { "id": "cdn-examples-ab-testing-as", "title": "A/B Testing — AssemblyScript (CDN)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-ab-testing-as.md", "skill": "fastedge-docs", "category": "examples", @@ -548,11 +548,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "testing", "cookie-based", "traffic", @@ -562,7 +562,7 @@ "requests", "different" ], - "content_sha256": "271da479486db22cd96d1891a8b2e5755921af0d4fd4c733df1732ef35e67684", + "content_sha256": "5fa3790d1f7f070a89f606e6999200868933dc65e36fe612d9a0095c6fe0ca1a", "sections": [ { "id": "cdn-examples-ab-testing-as#introduction", @@ -577,11 +577,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "testing", "assemblyscript", "cdn", @@ -990,6 +990,186 @@ "assignment", "logic" ] + }, + { + "id": "cdn-examples-ab-testing-as#source-material", + "heading": "Source Material", + "level": 2, + "anchor": "source-material", + "line_start": 228, + "line_end": 413, + "keywords": [ + "source", + "material", + "file", + "examples", + "abtesting", + "assembly", + "index.ts", + "export", + "gcoredev", + "proxy-wasm-sdk-as", + "proxy", + "import", + "context", + "filterheadersstatusvalues", + "get_property", + "log", + "loglevelvalues", + "registerrootcontext", + "rootcontext", + "send_http_response", + "set_property", + "stream_context", + "getenv", + "setloglevel", + "fastedge", + "getcurrenttime", + "pro" + ] + }, + { + "id": "cdn-examples-ab-testing-as#what-it-does", + "heading": "What it does", + "level": 2, + "anchor": "what-it-does", + "line_start": 414, + "line_end": 426, + "keywords": [ + "onrequestheaders", + "app", + "checks", + "existing", + "experiment", + "cookie", + "fe_exp_", + "experiment_name", + "found", + "assigns", + "user", + "variant", + "split", + "rewrites", + "request", + "path", + "prepending", + "variant-specific", + "prefix", + "e.g.", + "variant-a", + "original", + "adds", + "x-experiment", + "x-variant", + "headers", + "upstream", + "visibility.", + "onresponseheaders", + "sets", + "set-cookie", + "header", + "persist", + "assignment", + "sub" + ] + }, + { + "id": "cdn-examples-ab-testing-as#configuration", + "heading": "Configuration", + "level": 2, + "anchor": "configuration", + "line_start": 427, + "line_end": 438, + "keywords": [ + "configuration", + "set", + "following", + "environment", + "variables", + "your", + "fastedge", + "application", + "variable", + "example", + "description", + "----------", + "---------", + "-------------", + "experiment_name", + "homepage-redesign", + "name", + "experiment", + "required", + "variant_a_path", + "variant-a", + "path", + "prefix", + "variant", + "variant_b_path", + "variant-b", + "origin", + "server", + "serve", + "different", + "content", + "prefix." + ] + }, + { + "id": "cdn-examples-ab-testing-as#build", + "heading": "Build", + "level": 2, + "anchor": "build", + "line_start": 439, + "line_end": 452, + "keywords": [ + "build", + "pnpm", + "install", + "run", + "asbuild", + "output", + "file", + "description", + "------", + "-------------", + "abtesting.wasm", + "optimised", + "release", + "binary", + "upload", + "fastedge", + "abtesting-debug.wasm", + "debug", + "source", + "maps" + ] + }, + { + "id": "cdn-examples-ab-testing-as#deploy", + "heading": "Deploy", + "level": 2, + "anchor": "deploy", + "line_start": 453, + "line_end": 457, + "keywords": [ + "deploy", + "upload", + "build", + "abtesting.wasm", + "fastedge", + "portal", + "attach", + "your", + "cdn", + "application.", + "configure", + "experiment", + "environment", + "variables", + "application", + "settings." + ] } ] }, @@ -1652,7 +1832,7 @@ { "id": "cdn-examples-api-key-as", "title": "CDN Example: API Key Authentication (AssemblyScript)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-api-key-as.md", "skill": "fastedge-docs", "category": "examples", @@ -1672,11 +1852,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -1686,7 +1866,7 @@ "api-key-auth", "header-validation" ], - "content_sha256": "44acbfa72b450892ad45f4e360dd734b0a451a6df664428a2925e1419a6f8a6e", + "content_sha256": "af7f651d7d617a498e1776ce8c5ccf0e50fb0d53cb7431ecd40ddbbdea8aa5bf", "sections": [ { "id": "cdn-examples-api-key-as#introduction", @@ -1701,11 +1881,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -2060,6 +2240,176 @@ "deployment", "guide" ] + }, + { + "id": "cdn-examples-api-key-as#source-material", + "heading": "Source Material", + "level": 2, + "anchor": "source-material", + "line_start": 203, + "line_end": 326, + "keywords": [ + "source", + "material", + "file", + "examples", + "apikey", + "assembly", + "index.ts", + "export", + "gcoredev", + "proxy-wasm-sdk-as", + "proxy", + "import", + "context", + "filterheadersstatusvalues", + "headerpair", + "log", + "loglevelvalues", + "makeheaderpair", + "registerrootcontext", + "rootcontext", + "send_http_response", + "stream_context", + "getsecret", + "setloglevel", + "fastedge", + "const", + "unauthorized", + "u32", + "401", + "forbidd" + ] + }, + { + "id": "cdn-examples-api-key-as#what-it-does", + "heading": "What it does", + "level": 2, + "anchor": "what-it-does", + "line_start": 327, + "line_end": 340, + "keywords": [ + "onrequestheaders", + "app", + "reads", + "expected", + "api", + "key", + "api_key", + "secret.", + "checks", + "x-api-key", + "request", + "header.", + "returns", + "401", + "unauthorized", + "header", + "missing.", + "403", + "forbidden", + "match.", + "success", + "clears", + "forwarding", + "upstream", + "origin", + "proxy-wasm", + ".remove", + "sets", + "value", + "empty", + "string", + "rather", + "deleting", + "simpler", + "alternative", + "jwt", + "validation", + "you" + ] + }, + { + "id": "cdn-examples-api-key-as#configuration", + "heading": "Configuration", + "level": 2, + "anchor": "configuration", + "line_start": 341, + "line_end": 348, + "keywords": [ + "configuration", + "set", + "following", + "your", + "fastedge", + "application", + "name", + "type", + "description", + "------", + "-------------", + "api_key", + "secret", + "expected", + "api", + "key", + "value" + ] + }, + { + "id": "cdn-examples-api-key-as#build", + "heading": "Build", + "level": 2, + "anchor": "build", + "line_start": 349, + "line_end": 362, + "keywords": [ + "build", + "pnpm", + "install", + "run", + "asbuild", + "output", + "file", + "description", + "------", + "-------------", + "apikey.wasm", + "optimised", + "release", + "binary", + "upload", + "fastedge", + "apikey-debug.wasm", + "debug", + "source", + "maps" + ] + }, + { + "id": "cdn-examples-api-key-as#deploy", + "heading": "Deploy", + "level": 2, + "anchor": "deploy", + "line_start": 363, + "line_end": 367, + "keywords": [ + "deploy", + "upload", + "build", + "apikey.wasm", + "fastedge", + "portal", + "attach", + "your", + "cdn", + "application.", + "configure", + "api_key", + "secret", + "application", + "settings." + ] } ] }, @@ -2370,7 +2720,7 @@ { "id": "cdn-examples-auth-jwt-as", "title": "JWT Validation — CDN App (AssemblyScript)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 --> --- capabilities: - jwt-auth - request-header-inspection - secret-access - http-response type: example app_type: cdn languages: - assemblyscrip", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 --> --- capabilities: - jwt-auth - request-header-inspection - secret-access - http-response type: example app_type: cdn languages: - assemblyscrip", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-auth-jwt-as.md", "skill": "fastedge-docs", "category": "examples", @@ -2392,11 +2742,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "capabilities", "jwt-auth", @@ -2407,7 +2757,7 @@ "example", "app_type" ], - "content_sha256": "7defe19d2cd21c1429dbf522d5fe31e212ec1f4880b241375b44f179c2add564", + "content_sha256": "c3db56791c0fc27ab490143ec6bf139d25ed7516845c966bec320bb55f444c86", "sections": [ { "id": "cdn-examples-auth-jwt-as#introduction", @@ -2422,11 +2772,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "capabilities", "jwt-auth", @@ -3428,7 +3778,7 @@ { "id": "cdn-examples-body-as", "title": "Body Manipulation — AssemblyScript (CDN)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-body-as.md", "skill": "fastedge-docs", "category": "examples", @@ -3448,11 +3798,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "title", "body", @@ -3462,7 +3812,7 @@ "app_type", "languages" ], - "content_sha256": "cf0366213848652a381fe943b6a887700b4a939b885f9bb54dc3c6232f8b6ab0", + "content_sha256": "418f834e01924510268a0c8bb73e37ee660382ae3f31079a506b50e37b508188", "sections": [ { "id": "cdn-examples-body-as#introduction", @@ -3477,11 +3827,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "title", "body", @@ -3908,6 +4258,139 @@ "body", "buffering" ] + }, + { + "id": "cdn-examples-body-as#source-material", + "heading": "Source Material", + "level": 2, + "anchor": "source-material", + "line_start": 341, + "line_end": 519, + "keywords": [ + "source", + "material", + "file", + "examples", + "body", + "assembly", + "index.ts", + "export", + "gcoredev", + "proxy-wasm-sdk-as", + "proxy", + "exports", + "required", + "functions", + "interact", + "us.", + "import", + "buffertypevalues", + "context", + "filterdatastatusvalues", + "filterheadersstatusvalues", + "get_buffer_bytes", + "get_property", + "log", + "loglevelvalues", + "registerrootcontext", + "rootcontext", + "set_buffer_bytes", + "set_property", + "stream_context" + ] + }, + { + "id": "cdn-examples-body-as#what-it-does", + "heading": "What it does", + "level": 2, + "anchor": "what-it-does", + "line_start": 520, + "line_end": 534, + "keywords": [ + "onrequestheaders", + "removes", + "content-length", + "header", + "required", + "because", + "body", + "content", + "altered.", + "onrequestbody", + "buffers", + "full", + "request", + "checks", + "whether", + "contains", + "word", + "client", + "found", + "replaced", + "redaction", + "notice.", + "onresponseheaders", + "sets", + "transfer-encoding", + "chunked", + "captures", + "content-type", + "runtime", + "property.", + "onresponsebody", + "logs", + "url" + ] + }, + { + "id": "cdn-examples-body-as#build", + "heading": "Build", + "level": 2, + "anchor": "build", + "line_start": 535, + "line_end": 548, + "keywords": [ + "build", + "pnpm", + "install", + "run", + "asbuild", + "output", + "file", + "description", + "-----------------------", + "--------------------------------------------------", + "body.wasm", + "optimised", + "release", + "binary", + "upload", + "fastedge", + "body-debug.wasm", + "debug", + "source", + "maps" + ] + }, + { + "id": "cdn-examples-body-as#deploy", + "heading": "Deploy", + "level": 2, + "anchor": "deploy", + "line_start": 549, + "line_end": 553, + "keywords": [ + "deploy", + "upload", + "build", + "body.wasm", + "fastedge", + "portal", + "attach", + "your", + "cdn", + "application." + ] } ] }, @@ -4370,7 +4853,7 @@ { "id": "cdn-examples-cache-control-as", "title": "Cache Control — AssemblyScript (CDN)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-cache-control-as.md", "skill": "fastedge-docs", "category": "examples", @@ -4392,11 +4875,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -4406,7 +4889,7 @@ "cache-control", "response-headers" ], - "content_sha256": "0a153a37be4f5636fac31f8469be52c9af84394259760e897788b7654ba00fb9", + "content_sha256": "c8c82003063d7389e4b28b68f867419b749cee6c60771acb59942daeb598cb9a", "sections": [ { "id": "cdn-examples-cache-control-as#introduction", @@ -4421,11 +4904,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -6516,7 +6999,7 @@ { "id": "cdn-examples-cors-as", "title": "CORS — AssemblyScript (CDN)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-cors-as.md", "skill": "fastedge-docs", "category": "examples", @@ -6536,11 +7019,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "cors", "response-header", "injection", @@ -6550,7 +7033,7 @@ "proxy-wasm", "sdk." ], - "content_sha256": "204de98ec6e870b35f9cdd8f9b2a9d9b23c1e7152db0ee88fa8b62aaf3a5d02b", + "content_sha256": "d61cf87a36249c0ed5c044df6796a44ba3dec5f706d97c7d18bbaa4894bef9e1", "sections": [ { "id": "cdn-examples-cors-as#introduction", @@ -6565,11 +7048,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "cors", "assemblyscript", "cdn", @@ -7692,7 +8175,7 @@ { "id": "cdn-examples-custom-error-pages-as", "title": "Custom Error Pages — AssemblyScript (CDN)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-custom-error-pages-as.md", "skill": "fastedge-docs", "category": "examples", @@ -7712,11 +8195,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -7726,7 +8209,7 @@ "response-headers", "response-body" ], - "content_sha256": "db4273297015e84837b705d18e071b4684410000b7bc9fcf363fe82c1c58ea15", + "content_sha256": "4db5f9268da49b745f237677f4cba2a096cdd53f63cdad4dc043a01f343f9f7d", "sections": [ { "id": "cdn-examples-custom-error-pages-as#introduction", @@ -7741,11 +8224,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -8175,125 +8658,266 @@ "hook", "sequencing" ] - } - ] - }, - { - "id": "cdn-examples-custom-error-pages-rust", - "title": "cdn-examples-custom-error-pages-rust", - "description": "auto-updated: true sources: - id: fastedge-sdk-rust ref: main commit: 6eedcca9d5c0ddd4ff79ca475965393891da2d75 updated: 2026-09-22 -->", - "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-custom-error-pages-rust.md", - "skill": "fastedge-docs", - "category": "examples", - "app_types": [ - "cdn" - ], - "languages": [ - "rust" - ], - "tags": [ - "examples", - "fastedge-docs", - "cdn", - "rust", - "auto-updated", - "true", - "sources", - "fastedge-sdk-rust", - "ref", - "main", - "commit", - "6eedcca9d5c0ddd4ff79ca475965393891da2d75", - "updated", - "2026-09-22", - "custom", - "error", - "pages", - "overview", - "intercepts", - "4xx", - "5xx", - "http" - ], - "content_sha256": "efad33874c535871854509dd840e49c337a0b3f8a7971d0743d6d1eebff0826d", - "sections": [ + }, { - "id": "cdn-examples-custom-error-pages-rust#introduction", - "heading": "Introduction", - "level": 1, - "anchor": "introduction", - "line_start": 1, - "line_end": 9, + "id": "cdn-examples-custom-error-pages-as#source-material", + "heading": "Source Material", + "level": 2, + "anchor": "source-material", + "line_start": 296, + "line_end": 499, "keywords": [ - "auto-updated", - "true", - "sources", - "fastedge-sdk-rust", - "ref", - "main", - "commit", - "6eedcca9d5c0ddd4ff79ca475965393891da2d75", - "updated", - "2026-09-22" + "source", + "material", + "file", + "examples", + "customerrorpages", + "assembly", + "index.ts", + "export", + "gcoredev", + "proxy-wasm-sdk-as", + "proxy", + "import", + "context", + "filterdatastatusvalues", + "filterheadersstatusvalues", + "get_property", + "log", + "loglevelvalues", + "registerrootcontext", + "rootcontext", + "set_buffer_bytes", + "buffertypevalues", + "stream_context", + "setloglevel", + "fastedge", + "class", + "errorpagesroot" ] }, { - "id": "cdn-examples-custom-error-pages-rust#custom-error-pages-—-cdn-rust", - "heading": "Custom Error Pages — CDN (Rust)", + "id": "cdn-examples-custom-error-pages-as#what-it-does", + "heading": "What it does", "level": 2, - "anchor": "custom-error-pages-—-cdn-rust", - "line_start": 10, - "line_end": 297, + "anchor": "what-it-does", + "line_start": 500, + "line_end": 512, "keywords": [ - "custom", - "error", - "pages", - "cdn", - "rust", - "overview", - "intercepts", - "4xx", - "5xx", - "http", - "responses", - "edge", - "replaces", - "their", - "bodies", - "branded", + "onresponseheaders", + "app", + "reads", + "response.status", + "property.", + "400-599", + "range", + "sets", + "content-type", + "text", "html", - "rendered", - "via", - "handlebars", - "templates.", - "use", - "pattern", - "you", - "consistent", + "prepares", + "body", + "replacement.", + "onresponsebody", + "buffers", + "full", + "response", + "replaces", "styled", - "served", - "regardless", - "origin", - "returns.", + "page", + "containing", + "numeric", + "status", + "code", + "human-readable", + "error", + "title", + "e.g.", + "found", + "bad", + "gateway", + "description", + "explaining", + "went", + "wrong", + "category", + "label", + "client", + "server" + ] + }, + { + "id": "cdn-examples-custom-error-pages-as#build", + "heading": "Build", + "level": 2, + "anchor": "build", + "line_start": 513, + "line_end": 526, + "keywords": [ "build", - "script", - "build.rs", - "runs", - "compile", - "time", - "embed", - "images", - "messages", - "public" + "pnpm", + "install", + "run", + "asbuild", + "output", + "file", + "description", + "------", + "-------------", + "customerrorpages.wasm", + "optimised", + "release", + "binary", + "upload", + "fastedge", + "customerrorpages-debug.wasm", + "debug", + "source", + "maps" + ] + }, + { + "id": "cdn-examples-custom-error-pages-as#deploy", + "heading": "Deploy", + "level": 2, + "anchor": "deploy", + "line_start": 527, + "line_end": 531, + "keywords": [ + "deploy", + "upload", + "build", + "customerrorpages.wasm", + "fastedge", + "portal", + "attach", + "your", + "cdn", + "application.", + "environment", + "variables", + "secrets", + "required." ] } ] }, { - "id": "cdn-examples-custom-rust", - "title": "CDN Example: Custom — Rust", + "id": "cdn-examples-custom-error-pages-rust", + "title": "cdn-examples-custom-error-pages-rust", "description": "auto-updated: true sources: - id: fastedge-sdk-rust ref: main commit: 6eedcca9d5c0ddd4ff79ca475965393891da2d75 updated: 2026-09-22 -->", - "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-custom-rust.md", + "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-custom-error-pages-rust.md", + "skill": "fastedge-docs", + "category": "examples", + "app_types": [ + "cdn" + ], + "languages": [ + "rust" + ], + "tags": [ + "examples", + "fastedge-docs", + "cdn", + "rust", + "auto-updated", + "true", + "sources", + "fastedge-sdk-rust", + "ref", + "main", + "commit", + "6eedcca9d5c0ddd4ff79ca475965393891da2d75", + "updated", + "2026-09-22", + "custom", + "error", + "pages", + "overview", + "intercepts", + "4xx", + "5xx", + "http" + ], + "content_sha256": "efad33874c535871854509dd840e49c337a0b3f8a7971d0743d6d1eebff0826d", + "sections": [ + { + "id": "cdn-examples-custom-error-pages-rust#introduction", + "heading": "Introduction", + "level": 1, + "anchor": "introduction", + "line_start": 1, + "line_end": 9, + "keywords": [ + "auto-updated", + "true", + "sources", + "fastedge-sdk-rust", + "ref", + "main", + "commit", + "6eedcca9d5c0ddd4ff79ca475965393891da2d75", + "updated", + "2026-09-22" + ] + }, + { + "id": "cdn-examples-custom-error-pages-rust#custom-error-pages-—-cdn-rust", + "heading": "Custom Error Pages — CDN (Rust)", + "level": 2, + "anchor": "custom-error-pages-—-cdn-rust", + "line_start": 10, + "line_end": 297, + "keywords": [ + "custom", + "error", + "pages", + "cdn", + "rust", + "overview", + "intercepts", + "4xx", + "5xx", + "http", + "responses", + "edge", + "replaces", + "their", + "bodies", + "branded", + "html", + "rendered", + "via", + "handlebars", + "templates.", + "use", + "pattern", + "you", + "consistent", + "styled", + "served", + "regardless", + "origin", + "returns.", + "build", + "script", + "build.rs", + "runs", + "compile", + "time", + "embed", + "images", + "messages", + "public" + ] + } + ] + }, + { + "id": "cdn-examples-custom-rust", + "title": "CDN Example: Custom — Rust", + "description": "auto-updated: true sources: - id: fastedge-sdk-rust ref: main commit: 6eedcca9d5c0ddd4ff79ca475965393891da2d75 updated: 2026-09-22 -->", + "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-custom-rust.md", "skill": "fastedge-docs", "category": "examples", "app_types": [ @@ -8733,7 +9357,7 @@ { "id": "cdn-examples-env-secrets-as", "title": "Environment Variables and Secrets — CDN (AssemblyScript)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 --> --- type: example app_type: cdn languages: [as] capabilities: [env-vars, secrets] ---", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 --> --- type: example app_type: cdn languages: [as] capabilities: [env-vars, secrets] ---", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-env-secrets-as.md", "skill": "fastedge-docs", "category": "examples", @@ -8753,11 +9377,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -8768,7 +9392,7 @@ "secrets", "environment" ], - "content_sha256": "3c64cf405e55a27101a1b957168091583141e9c42e286db01b35cc3770d77601", + "content_sha256": "97297fd0847bec88008f332984d685818a0c9bbd39ceb6e7fcfdd36a8310f413", "sections": [ { "id": "cdn-examples-env-secrets-as#introduction", @@ -8783,11 +9407,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -9198,6 +9822,221 @@ "handling", "guidelines" ] + }, + { + "id": "cdn-examples-env-secrets-as#source-material", + "heading": "Source Material", + "level": 2, + "anchor": "source-material", + "line_start": 229, + "line_end": 313, + "keywords": [ + "source", + "material", + "file", + "examples", + "variablesandsecrets", + "assembly", + "index.ts", + "export", + "gcoredev", + "proxy-wasm-sdk-as", + "proxy", + "exports", + "required", + "functions", + "interact", + "us.", + "import", + "context", + "filterheadersstatusvalues", + "log", + "loglevelvalues", + "registerrootcontext", + "rootcontext", + "stream_context", + "getenv", + "getsecret", + "setloglevel", + "fastedge" + ] + }, + { + "id": "cdn-examples-env-secrets-as#what-it-does", + "heading": "What it does", + "level": 2, + "anchor": "what-it-does", + "line_start": 314, + "line_end": 326, + "keywords": [ + "onrequestheaders", + "app", + "reads", + "username", + "environment", + "variable", + "using", + "getenv", + "password", + "secret", + "getsecret", + "logs", + "values", + "retrieved", + "without", + "logging", + "value", + "itself", + "injects", + "them", + "x-env-username", + "x-env-password", + "request", + "headers", + "upstream", + "receives", + "them.", + "useful", + "reference", + "understanding", + "access", + "variables", + "secrets", + "within", + "fastedge", + "plugin.", + "security", + "warning", + "neve" + ] + }, + { + "id": "cdn-examples-env-secrets-as#configuration", + "heading": "Configuration", + "level": 2, + "anchor": "configuration", + "line_start": 327, + "line_end": 335, + "keywords": [ + "configuration", + "set", + "following", + "your", + "fastedge", + "application", + "name", + "type", + "description", + "----------", + "--------------------", + "--------------------------------------", + "username", + "environment", + "variable", + "value", + "forward", + "upstream", + "password", + "secret" + ] + }, + { + "id": "cdn-examples-env-secrets-as#local-testing", + "heading": "Local testing", + "level": 2, + "anchor": "local-testing", + "line_start": 336, + "line_end": 346, + "keywords": [ + "local", + "testing", + "fixture", + "fixtures", + "happy-path.test.json", + "uses", + "dotenv", + "enabled", + "true", + "load", + "values", + ".env", + "runner", + "maps", + "fastedge_var_env_", + "name", + "getenv", + "fastedge_var_secret_", + "getsecret", + "test", + "locally", + "visual", + "debugger", + "create", + "fastedge_var_env_username", + "my-username", + "fastedge_var_secret_password", + "my-password" + ] + }, + { + "id": "cdn-examples-env-secrets-as#build", + "heading": "Build", + "level": 2, + "anchor": "build", + "line_start": 347, + "line_end": 360, + "keywords": [ + "build", + "pnpm", + "install", + "run", + "asbuild", + "output", + "file", + "description", + "--------------------------------------", + "--------------------------------------------------", + "variablesandsecrets.wasm", + "optimised", + "release", + "binary", + "upload", + "fastedge", + "variablesandsecrets-debug.wasm", + "debug", + "source", + "maps" + ] + }, + { + "id": "cdn-examples-env-secrets-as#deploy", + "heading": "Deploy", + "level": 2, + "anchor": "deploy", + "line_start": 361, + "line_end": 365, + "keywords": [ + "deploy", + "upload", + "build", + "variablesandsecrets.wasm", + "fastedge", + "portal", + "https", + "portal.gcore.com", + "attach", + "your", + "cdn", + "application.", + "configure", + "username", + "environment", + "variable", + "password", + "secret", + "application", + "settings." + ] } ] }, @@ -9660,7 +10499,7 @@ { "id": "cdn-examples-geo-redirect-as", "title": "Geo Redirect — CDN (AssemblyScript)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-geo-redirect-as.md", "skill": "fastedge-docs", "category": "examples", @@ -9680,11 +10519,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -9694,7 +10533,7 @@ "geo-routing", "header-manipulation" ], - "content_sha256": "bfd821f4dcef5d6f8a06638c0f353365ad97cccd76619f27e6b5fc8ad6e1d48a", + "content_sha256": "0fae7d0336a410a3b10e24eb74d900cce71e9ae556cb298ff7ee37383e439c68", "sections": [ { "id": "cdn-examples-geo-redirect-as#introduction", @@ -9709,11 +10548,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -10841,7 +11680,7 @@ { "id": "cdn-examples-geoblock-as", "title": "Geoblock — CDN App Example (AssemblyScript)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-geoblock-as.md", "skill": "fastedge-docs", "category": "examples", @@ -10861,11 +11700,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -10875,7 +11714,7 @@ "geoblock", "geo-filtering" ], - "content_sha256": "763b705d33e44d10af681bf94505be744a28c35e28877e80f0b077006f56f37d", + "content_sha256": "148b1a90f16f51dee936e29aee1b426c24aba0d423ee86eefc23353373250199", "sections": [ { "id": "cdn-examples-geoblock-as#introduction", @@ -10890,11 +11729,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -11900,7 +12739,7 @@ { "id": "cdn-examples-headers-as", "title": "CDN Headers Manipulation — AssemblyScript", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-headers-as.md", "skill": "fastedge-docs", "category": "examples", @@ -11922,11 +12761,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -11936,7 +12775,7 @@ "headers", "request-headers" ], - "content_sha256": "eb268cb0cde0417bb082dbadb140da4f26f837195f2c6d93a907bc1643fc243e", + "content_sha256": "f4799cc1d18e1e473a374b576a6be37324a4d3f5957d0957a136e10508249d0c", "sections": [ { "id": "cdn-examples-headers-as#introduction", @@ -11951,11 +12790,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -12093,11 +12932,11 @@ "method", "signature", "description", - "--------------------------", + "----------------------", "---------------------------------------", - "-----------------------------------------------------------------------------------------------------", + "---------------------------------------------------------------------------------------------", "get", - "nam" + "name" ] }, { @@ -12934,7 +13773,7 @@ { "id": "cdn-examples-http-call-as", "title": "cdn-examples-http-call-as", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-http-call-as.md", "skill": "fastedge-docs", "category": "examples", @@ -12956,11 +13795,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "overview", "httpcall", "dispatches", @@ -12971,7 +13810,7 @@ "fastedge", "proxy-wasm" ], - "content_sha256": "5fd3877e09d4896bd6c69ed4c0cb9cc56dc6cd0d8fadb01c02391285f4b8bce6", + "content_sha256": "534a12e5822a9394bae1f5757847a68ad870ab3f5eb02922c6f2b2cb3235efd9", "sections": [ { "id": "cdn-examples-http-call-as#introduction", @@ -12986,11 +13825,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22" + "2026-09-29" ] }, { @@ -13039,7 +13878,7 @@ "level": 2, "anchor": "api-patterns", "line_start": 14, - "line_end": 101, + "line_end": 103, "keywords": [ "api", "patterns", @@ -13088,8 +13927,8 @@ "heading": "Common Patterns", "level": 2, "anchor": "common-patterns", - "line_start": 102, - "line_end": 286, + "line_start": 104, + "line_end": 288, "keywords": [ "common", "patterns", @@ -13126,8 +13965,8 @@ "heading": "Build", "level": 2, "anchor": "build", - "line_start": 287, - "line_end": 306, + "line_start": 289, + "line_end": 308, "keywords": [ "build", "pnpm", @@ -13173,8 +14012,8 @@ "heading": "Gotchas", "level": 2, "anchor": "gotchas", - "line_start": 307, - "line_end": 318, + "line_start": 309, + "line_end": 320, "keywords": [ "gotchas", "closures", @@ -13218,8 +14057,8 @@ "heading": "Related", "level": 2, "anchor": "related", - "line_start": 319, - "line_end": 324, + "line_start": 321, + "line_end": 326, "keywords": [ "related", "sdk", @@ -13841,7 +14680,7 @@ { "id": "cdn-examples-kv-store-as", "title": "KV Store — AssemblyScript CDN Example", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-kv-store-as.md", "skill": "fastedge-docs", "category": "examples", @@ -13863,11 +14702,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -13877,7 +14716,7 @@ "kv-store", "sorted-sets" ], - "content_sha256": "bfdb069b5c22829eb076f681740fba8b7d13263e17108b9905e3e513a3ae7350", + "content_sha256": "e194faae8b3bd9ff03f607ceb808511caca96daa2428cfa259c45d92e9d7cb83", "sections": [ { "id": "cdn-examples-kv-store-as#introduction", @@ -13892,11 +14731,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -15054,7 +15893,7 @@ { "id": "cdn-examples-large-dictionary-as", "title": "Large Dictionary — AssemblyScript (CDN)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-large-dictionary-as.md", "skill": "fastedge-docs", "category": "examples", @@ -15074,11 +15913,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "large", "dictionary", "overview", @@ -15087,7 +15926,7 @@ "environment", "variables" ], - "content_sha256": "06c3e8963a2738ac48fbbfbf6217cded6950009199039727a0ed53963ab8474d", + "content_sha256": "669d8a1223ebb90b395c33eccbec9eed8b6e24c3d6de414d70dfa49bf3cadf0f", "sections": [ { "id": "cdn-examples-large-dictionary-as#introduction", @@ -15102,11 +15941,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "large", "dictionary", "assemblyscript", @@ -15199,7 +16038,7 @@ "level": 2, "anchor": "pattern-read-in-onrequestheaders", "line_start": 45, - "line_end": 88, + "line_end": 92, "keywords": [ "pattern", "read", @@ -15237,8 +16076,8 @@ "heading": "Environment Variables", "level": 2, "anchor": "environment-variables", - "line_start": 89, - "line_end": 94, + "line_start": 93, + "line_end": 98, "keywords": [ "environment", "variables", @@ -15267,8 +16106,8 @@ "heading": "Build", "level": 2, "anchor": "build", - "line_start": 95, - "line_end": 121, + "line_start": 99, + "line_end": 125, "keywords": [ "build", "package", @@ -15306,8 +16145,8 @@ "heading": "Deployment", "level": 2, "anchor": "deployment", - "line_start": 122, - "line_end": 125, + "line_start": 126, + "line_end": 129, "keywords": [ "deployment", "upload", @@ -15332,8 +16171,8 @@ "heading": "Gotchas", "level": 2, "anchor": "gotchas", - "line_start": 126, - "line_end": 133, + "line_start": 130, + "line_end": 137, "keywords": [ "gotchas", "empty", @@ -15382,8 +16221,8 @@ "heading": "See Also", "level": 2, "anchor": "see-also", - "line_start": 134, - "line_end": 139, + "line_start": 138, + "line_end": 143, "keywords": [ "see", "getenv", @@ -15410,205 +16249,6 @@ "patterns", "fastedge-docs" ] - }, - { - "id": "cdn-examples-large-dictionary-as#source-material", - "heading": "Source Material", - "level": 2, - "anchor": "source-material", - "line_start": 140, - "line_end": 223, - "keywords": [ - "source", - "material", - "file", - "examples", - "largedictionary", - "assembly", - "index.ts", - "export", - "gcoredev", - "proxy-wasm-sdk-as", - "proxy", - "import", - "context", - "filterheadersstatusvalues", - "log", - "loglevelvalues", - "registerrootcontext", - "rootcontext", - "stream_context", - "getdictionary", - "setloglevel", - "fastedge", - "class", - "largedictionaryroot", - "extends", - "createcontext", - "context_id", - "u32", - "con" - ] - }, - { - "id": "cdn-examples-large-dictionary-as#when-to-use-getdictionary-vs-getenv", - "heading": "When to use `getDictionary` vs `getEnv`", - "level": 2, - "anchor": "when-to-use-getdictionary-vs-getenv", - "line_start": 224, - "line_end": 234, - "keywords": [ - "use", - "getdictionary", - "getenv", - "function", - "----------", - "name", - "variable", - "value", - "under", - "cases", - "exceed", - "wasi", - "env", - "var", - "size", - "limit", - "environment", - "interface", - "per", - "variable.", - "your", - "app", - "needs", - "read", - "larger", - "values", - "e.g.", - "large", - "json", - "configs", - "pem", - "certificates", - "policy", - "documents", - "calls", - "proxy_dictionary_get", - "bypasses", - "thi" - ] - }, - { - "id": "cdn-examples-large-dictionary-as#what-it-does", - "heading": "What it does", - "level": 2, - "anchor": "what-it-does", - "line_start": 235, - "line_end": 238, - "keywords": [ - "onrequestheaders", - "app", - "reads", - "large_config", - "environment", - "variable", - "using", - "getdictionary", - "logs", - "size", - "adds", - "x-config-size", - "request", - "header", - "upstream", - "see." - ] - }, - { - "id": "cdn-examples-large-dictionary-as#configuration", - "heading": "Configuration", - "level": 2, - "anchor": "configuration", - "line_start": 239, - "line_end": 246, - "keywords": [ - "configuration", - "set", - "following", - "your", - "fastedge", - "application", - "name", - "type", - "description", - "------", - "-------------", - "large_config", - "environment", - "variable", - "large", - "payload", - "e.g.", - "json", - "pem", - "certificate" - ] - }, - { - "id": "cdn-examples-large-dictionary-as#build", - "heading": "Build", - "level": 2, - "anchor": "build", - "line_start": 247, - "line_end": 260, - "keywords": [ - "build", - "pnpm", - "install", - "run", - "asbuild", - "output", - "file", - "description", - "------", - "-------------", - "largedictionary.wasm", - "optimised", - "release", - "binary", - "upload", - "fastedge", - "largedictionary-debug.wasm", - "debug", - "source", - "maps" - ] - }, - { - "id": "cdn-examples-large-dictionary-as#deploy", - "heading": "Deploy", - "level": 2, - "anchor": "deploy", - "line_start": 261, - "line_end": 265, - "keywords": [ - "deploy", - "upload", - "build", - "largedictionary.wasm", - "fastedge", - "portal", - "attach", - "your", - "cdn", - "application.", - "configure", - "large_config", - "environment", - "variable", - "application", - "settings." - ] } ] }, @@ -15735,7 +16375,7 @@ { "id": "cdn-examples-log-time-as", "title": "Log Time — CDN App (AssemblyScript)", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-log-time-as.md", "skill": "fastedge-docs", "category": "examples", @@ -15757,11 +16397,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -15771,7 +16411,7 @@ "logging", "time" ], - "content_sha256": "5266245f16c9a92efe159d422b1d6efbb80268cecbd4be0e9cbf62f79073c1d5", + "content_sha256": "e34afff4c3056a830b477e83f3ae14a1c2895d1006d1fccd39d43dd99ef63b6b", "sections": [ { "id": "cdn-examples-log-time-as#introduction", @@ -15786,11 +16426,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "---", "type", "example", @@ -17007,7 +17647,7 @@ { "id": "cdn-examples-properties-as", "title": "CDN Runtime Properties — AssemblyScript", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/cdn/examples-properties-as.md", "skill": "fastedge-docs", "category": "examples", @@ -17027,11 +17667,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "runtime", "properties", "language", @@ -17041,7 +17681,7 @@ "type", "overview" ], - "content_sha256": "3bbe18f8ddfeb71ed284002040e77bd67ef56bb6a5281f9165ebb30794f1cf43", + "content_sha256": "95826b68e601843c570b7ea017b8a1fb9316725e5f967cb69add4787ed058d75", "sections": [ { "id": "cdn-examples-properties-as#introduction", @@ -17056,11 +17696,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "cdn", "runtime", "properties", @@ -39419,7 +40059,7 @@ { "id": "quickstart-as", "title": "Quickstart: AssemblyScript CDN Apps on FastEdge", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/quickstart-as.md", "skill": "fastedge-docs", "category": "reference", @@ -39438,11 +40078,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "quickstart", "cdn", "apps", @@ -39453,7 +40093,7 @@ "compile", "webassembly" ], - "content_sha256": "44a67860b9459a777487ccc86b1f45572d7be137091e04b009c1cda5acfd6676", + "content_sha256": "730e4afc6dd0148b7d63428568f0f9d48aba566add4ffdd4fc0927607fff2b12", "sections": [ { "id": "quickstart-as#introduction", @@ -39468,11 +40108,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "quickstart", "assemblyscript", "cdn", @@ -39527,7 +40167,7 @@ "dependencies", "gcoredev", "proxy-wasm-sdk-as", - "1.2.3", + "1.2.4", "--save-dev", "assemblyscript", "0.28.9", @@ -40939,7 +41579,7 @@ { "id": "sdk-reference-as", "title": "AssemblyScript Proxy-Wasm SDK Reference", - "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: master commit: ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31 updated: 2026-09-22 -->", + "description": "auto-updated: true sources: - id: proxy-wasm-sdk-as ref: v1.2.4 commit: 073f583217448aaf25cb98f5d336a0fda26273ba updated: 2026-09-29 -->", "path": "plugins/gcore-fastedge/skills/fastedge-docs/reference/sdk-reference-as.md", "skill": "fastedge-docs", "category": "sdk", @@ -40958,11 +41598,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "proxy-wasm", "reference", "gcoredev", @@ -40972,7 +41612,7 @@ "writing", "cdn" ], - "content_sha256": "a14b716b83a08310d5fea5c9097ebefc7fece6329111d0f21c21b97751bd7093", + "content_sha256": "f002d1d4536a6d536fe5602abdb3b9d9ee45e0cc3927758df585d459735bf821", "sections": [ { "id": "sdk-reference-as#introduction", @@ -40987,11 +41627,11 @@ "sources", "proxy-wasm-sdk-as", "ref", - "master", + "v1.2.4", "commit", - "ddb8d2e76e8f4fab30de6bce8dd0150231cd7d31", + "073f583217448aaf25cb98f5d336a0fda26273ba", "updated", - "2026-09-22", + "2026-09-29", "assemblyscript", "proxy-wasm", "sdk",