{"id":21130607,"url":"https://github.com/golemcloud/golem-launch-demo","last_synced_at":"2025-06-10T20:04:19.686Z","repository":{"id":254490413,"uuid":"845542464","full_name":"golemcloud/golem-launch-demo","owner":"golemcloud","description":null,"archived":false,"fork":false,"pushed_at":"2024-12-09T15:25:55.000Z","size":12714,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-03-14T12:14:13.931Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/golemcloud.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-21T12:59:45.000Z","updated_at":"2024-12-09T15:26:41.000Z","dependencies_parsed_at":"2025-01-21T06:21:18.949Z","dependency_job_id":null,"html_url":"https://github.com/golemcloud/golem-launch-demo","commit_stats":null,"previous_names":["golemcloud/golem-launch-demo"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/golemcloud%2Fgolem-launch-demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/golemcloud%2Fgolem-launch-demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/golemcloud%2Fgolem-launch-demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/golemcloud%2Fgolem-launch-demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/golemcloud","download_url":"https://codeload.github.com/golemcloud/golem-launch-demo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/golemcloud%2Fgolem-launch-demo/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259143540,"owners_count":22811903,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-20T05:35:37.187Z","updated_at":"2025-06-10T20:04:19.632Z","avatar_url":"https://github.com/golemcloud.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Golem 1.0 Launch Demo\n\n## Description\nIn this demo we implement a **collaborative list editor application** using Golem. The application can handle an arbitrary number of simultaneously open **lists** - each list consists of a list of string items. These list items can be appended, inserted and deleted simultaneously by multiple users; the current list state can be queried any time, as well as the active \"editor\" connections. Modification is only allowed for connected editors, and there is a `poll` function available for them which only returns the new changes since the last call.\n\nLists can be archived, in which case they are no longer editable and their contents are saved in a separate **list archive**. Then the list can be deleted, it's last state remains forever in archive.\n\nAn additional feature is that if a list is **not archived** and there are no changes for a certain period of time, all the connected editors are notified by sending an email to them. (Note: the demo does not actually implement the email sending, just prints a log line where it would do so.)\n\n## Demo script\n\n### Phase 1\nIn phase 1 we create the first version of the `lst` component, where each worker represents a stateful list and provides some basic manipulation and query functionalities.\n\nCreate the new component:\n\n```zsh\ngolem-cloud-cli new --lang ts --package-name demo:lst lst\n```\n\nCompile the initial version:\n\n```zsh\ncd lst\nnpm install\nnpm run componentize\n```\n\nWe have a Golem component in `out/lst.wasm.`.\n\nApply the required changes to the WIT file (`prepared/phase-1/lst/wit/main.wit`) and regenerate the bindings:\n\n```zsh\nnpm run componentize\n```\n\nThen implement the first version (`prepared/phase-1/lst/src/main.ts`) and compile again:\n\n```zsh\nnpm run componentize\n```\n\nLet's set the **project ID** we are working with (Golem Cloud only) to an environment variable:\n\n```zsh\nexport PRJ=urn:project:b17d7bbf-9704-4578-bc25-8b1ad22f3f3a # Live demo project\n```\n\nDeploy the component and store it's ID in an environment variable:\n\n```zsh\ngolem-cloud-cli component add --project $PRJ --component-name lst out/lst.wasm\nexport LST=urn:component:4a3d6c13-9086-43d6-88c6-be4faeedc1f7\n```\n\nTry it out:\n\n```zsh\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test1 --function 'demo:lst/api.{add}' --arg '\"item 1\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test1 --function 'demo:lst/api.{add}' --arg '\"item 3\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test1 --function 'demo:lst/api.{insert}' --arg '\"item 1\"' --arg '\"item 2\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test1 --function 'demo:lst/api.{get}'\n```\n\n### Phase 2\nIn phase 2 we add the concept of a `connection` and polling.\n\nFirst we modify the WIT file (`prepared/phase-2/lst/wit/main.wit`) and regenerate the bindings:\n\n```zsh\nnpm run componentize\n```\n\nThen implement the changes (`prepared/phase-2/lst/src/main.ts`)\n- Define `EditorState`\n- Add a `connected` map and a `lastConnectionId` variable to `State`\n- Write two helper functions: `isConnected` and `addEvent`\n- Modify the existing exported functions\n- Write the new ones\n\nand compile again:\n\n```zsh\nnpm run componentize\n```\n\nUpdate the project:\n\n```zsh\ngolem-cloud-cli component update --component $LST out/lst.wasm\n```\n\nNote that this did not update the existing worker, but new workers will use the new version.\n\nTry it out (using a new worker name to use the updated version):\n\n```zsh\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test2 --function 'demo:lst/api.{connect}' --arg '\"vigoo@golem.cloud\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test2 --function 'demo:lst/api.{connect}' --arg '\"john@golem.cloud\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test2 --function 'demo:lst/api.{add}' --arg '{id: 1}' --arg '\"item 1\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test2 --function 'demo:lst/api.{add}' --arg '{id: 1}' --arg '\"item 3\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test2 --function 'demo:lst/api.{insert}' --arg '{id: 2}' --arg '\"item 1\"' --arg '\"item 2\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test2 --function 'demo:lst/api.{get}'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test2 --function 'demo:lst/api.{connected-editors}'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test2 --function 'demo:lst/api.{poll}' --arg '{id: 1}'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test2 --function 'demo:lst/api.{poll}' --arg '{id: 2}'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test2 --function 'demo:lst/api.{add}' --arg '{id: 1}' --arg '\"item 4\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test2 --function 'demo:lst/api.{poll}' --arg '{id: 1}'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test2 --function 'demo:lst/api.{add}' --arg '{id: 1}' --arg '\"item 5\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test2 --function 'demo:lst/api.{poll}' --arg '{id: 1}'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test2 --function 'demo:lst/api.{poll}' --arg '{id: 2}'\n\n```\n\n### Phase 3\nIn this step we implement the **archive functionality**.\n\nFirst let's create a new component, now using the Go language:\n\n```zsh\ngolem-cloud-cli new --lang go --package-name demo:archive archive\n```\n\ncompile the initial version:\n\n```zsh\ncd archive\nmake build\n```\n\nWrite the `archive` component's WIT definition (`prepared/phase-3/archive/wit/archive.wit`) and regenerate the bindings:\n\n```zsh\nmake bindings\n```\n\nThen implement it (`prepared/phase-3/archive/src/main.go`) and compile again:\n\n```zsh\nmake build\n```\n\nGenerate a **stub** for the `archive` component:\n\n```zsh\ncd ..\ngolem-cloud-cli stubgen build --source-wit-root archive/wit --dest-wasm archive-stub/archive-stub.wasm --dest-wit-root archive-stub/wit\n```\n\nAnd add the stub as a dependency to `lst`:\n\n```zsh\ngolem-cloud-cli stubgen add-stub-dependency --stub-wit-root archive-stub/wit --dest-wit-root lst/wit --overwrite\n```\n\nSee how the `wit/deps` directory now contains `demo-archive-stub`. Modify the `lst` WIT definition to include the stub, and to export archive functionality (`prepared/phase-3/lst/wit/main.wit`).\n\nRegenerate bindings for `lst`:\n\n```zsh\ncd lst\nnpm run componentize\n```\n\nImplement the archive feature (`prepared/phase-3/lst/src/main.ts`):\n- Add an `archive` flag to `State`\n- Modify `add`, `delete` and `insert` to check it\n- Implement `archive` and `isArchived`\n\nCompile the `lst` component:\n\n```zsh\nnpm run componentize\n```\n\nGet back to the root and compose the `lst.wasm` with the `archive-stub.wasm`:\n\n```zsh\ncd ..\ngolem-cloud-cli stubgen compose --source-wasm lst/out/lst.wasm --stub-wasm archive-stub/archive-stub.wasm --dest-wasm lst/out/lst-composed.wasm\n```\n\nBefore trying it out, first upload the new archive component and save it's URN and ID:\n\n```zsh\ngolem-cloud-cli component add --project $PRJ --component-name archive archive/archive.wasm\nexport ARCHIVE=urn:component:c95c8c49-db39-4221-8721-f1f2b7e02a9d\nexport ARCHIVE_ID=c95c8c49-db39-4221-8721-f1f2b7e02a9d\n```\n\nThen update the list component with the new, composed version:\n\n```zsh\ngolem-cloud-cli component update --component $LST lst/out/lst-composed.wasm\n```\n\nAnd try it out!\nFirst we explicitly create a new list, passing the archive component's ID:\n\n```zsh\ngolem-cloud-cli worker start --component $LST --worker-name test3 --env \"ARCHIVE_COMPONENT_ID=$ARCHIVE_ID\"\n```\n\nThen invoke it a few times, then query if it's archived:\n\n```zsh\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test3 --function 'demo:lst/api.{connect}' --arg '\"vigoo@golem.cloud\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test3 --function 'demo:lst/api.{add}' --arg '{id: 1}' --arg '\"item 1\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test3 --function 'demo:lst/api.{add}' --arg '{id: 1}' --arg '\"item 3\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test3 --function 'demo:lst/api.{get}'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test3 --function 'demo:lst/api.{is-archived}'\n```\n\nAt this point the archive worker does not exist yet:\n\n```zsh\ngolem-cloud-cli worker list --component $ARCHIVE\n```\n\nLet's archive our list:\n\n```zsh\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test3 --function 'demo:lst/api.{archive}'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test3 --function 'demo:lst/api.{is-archived}'\n```\n\nAnd see the archive worker:\n\n```zsh\ngolem-cloud-cli worker list --component $ARCHIVE\n```\n\nTry to query it:\n\n```zsh\ngolem-cloud-cli worker invoke-and-await --component $ARCHIVE --worker-name archive --function 'demo:archive/api.{get-all}'\n```\n\n### Phase 4\nIn the last step we implement the **email sending** functionality.\n\nFirst we create a new component, this time using rust:\n\n```zsh\ngolem-cli new --lang rust --package-name demo:email email\ncd email\ncargo component build --release\n```\n\nBefore we implement the `email` component, we are going to need to expose some functions from `lst` to be called from `email`.\n\nAdd the `email-query` interface to `lst`'s WIT, then run\n\n```\ncd lst\nnpm run componentize\n```\n\nThen implement the two new functions in `main.ts` and compile it.\n- Add the new `emailQuery` global\n- Add `emailDeadline` and `emailRecipients` to `State`\n- Add the `updateEmailProperties` helper function\n- Call it from `add`, `insert`, `delete`, `connect` and `disconnect`\n\nSee if it compiles:\n\n```zsh\nnpm run componentize\n```\n\nNow we can implement the `email` component.\n\nFirst we have to generate a **stub** for `lst`, so it can be called **from** `email`:\n\n```zsh\ncd ..\ngolem-cloud-cli stubgen build --source-wit-root lst/wit --dest-wasm lst-stub/lst-stub.wasm --dest-wit-root lst-stub/wit\n```\n\nThen add `lst` as a dependency of `email`:\n\n```zsh\ngolem-cloud-cli stubgen add-stub-dependency --stub-wit-root lst-stub/wit --dest-wit-root email/wit --overwrite --update-cargo-toml\n```\n\nTry to build the `email` component:\n\n```zsh\ncd email\ncargo component build --release\n```\n\nIt still compiles. See that `wit/deps` now contains `demo_lst-stub` and import it into `email/wit/email.wit` like we did with the archive stub before, then add it's API and regenerate the bindings:\n\n```zsh\ncargo component build --release\n```\n\nNow it fails so implement it (`prepared/phase-4/email/src/lib.rs`) and build\n\n```zsh\ncargo component build --release\n```\n\nCompose the result with the `lst` component's stub:\n\n```zsh\ncd ..\ngolem-cloud-cli stubgen compose --source-wasm email/target/wasm32-wasi/release/email.wasm --stub-wasm lst-stub/lst-stub.wasm --dest-wasm email/target/wasm32-wasi/release/email-composed.wasm\n```\n\nAt this point we have an `email` component but nobody calls it. We want to call it from the `lst` component whenever a new list is created.\nSo we first need to generate a stub for `email`, so it can be called **from** `lst`:\n\n```zsh\ngolem-cloud-cli stubgen build --source-wit-root email/wit --dest-wasm email-stub/email-stub.wasm --dest-wit-root email-stub/wit\n```\n\nThen add `email` as a dependency of `lst`:\n\n```zsh\ngolem-cloud-cli stubgen add-stub-dependency --stub-wit-root email-stub/wit --dest-wit-root lst/wit --overwrite\n```\n\nImport the `stub-email` interface in `lst/wit/main.wit` and regenerate the bindings:\n\n```zsh\ncd lst\nnpm run componentize\n```\n\nBecause we don't have a better place to spawn the email component, we create an `ensureInitialized` method on `State` and call it from each exported function.\n- Add the `initialized`, `name`, `emailComponentId` fields\n- Add the method\n- Call it from each exported function\n\nCompile it\n\n```zsh\nnpm run componentize\n```\n\nThen compose it with both the archive and the email stubs:\n\n```zsh\ncd ..\ngolem-cloud-cli stubgen compose --source-wasm lst/out/lst.wasm --stub-wasm archive-stub/archive-stub.wasm --dest-wasm lst/out/lst-composed1.wasm\ngolem-cloud-cli stubgen compose --source-wasm lst/out/lst-composed1.wasm --stub-wasm email-stub/email-stub.wasm --dest-wasm lst/out/lst-composed.wasm\n```\n\nBefore trying it out, first we upload the email component to the cloud:\n\n```zsh\ngolem-cloud-cli component add --project $PRJ --component-name email email/target/wasm32-wasi/release/email-composed.wasm\nexport EMAIL_ID=59163ee3-95a2-4e35-b660-9feaee1e2163\n```\n\nThen we update the `lst` component with the composed WASM:\n\n```zsh\ngolem-cloud-cli component update --component $LST lst/out/lst-composed.wasm\n```\n\nCreate a new list, now passing the email component id too:\n\n```zsh\ngolem-cloud-cli worker start --component $LST --worker-name test4 --env \"ARCHIVE_COMPONENT_ID=$ARCHIVE_ID\" --env \"EMAIL_COMPONENT_ID=$EMAIL_ID\"\n```\n\nEdit the list:\n\n```zsh\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test4 --function 'demo:lst/api.{connect}' --arg '\"vigoo@golem.cloud\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test4 --function 'demo:lst/api.{add}' --arg '{id: 1}' --arg '\"item 1\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test4 --function 'demo:lst/api.{add}' --arg '{id: 1}' --arg '\"item 3\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test4 --function 'demo:lst/api.{get}'\n```\n\nSee if it spawned the email worker:\n\n```zsh\ngolem-cloud-cli worker list --component urn:component:$EMAIL_ID\n```\n\nIt shows it's `Suspended`, because it's sleeping until the deadline is reached.\n\nWe can also check it's logs\n\n```zsh\ngolem-cloud-cli worker connect --component urn:component:$EMAIL_ID --worker-name test4\n```\n\nEnd.\n\n## Demo Script for 1.1\n\nCreate a new project\n```zsh\ngolem-cloud-cli project add --project-name 'Golem 1.1 Demo'\n```\n\nSave it's urn:\n\n```zsh\nexport PRJ=urn:project:629fa7d7-84e5-4215-ba5c-7fd383ae8b40\n```\n\nUpload the components created on the 1.0 launch:\n\n```zsh\ngolem-cloud-cli component add --project $PRJ --component-name email precompiled/email-composed.wasm\nexport EMAIL_ID=064e104e-be4f-4fb8-935b-c4c5a775eac1\ngolem-cloud-cli component add --project $PRJ --component-name lst precompiled/lst-composed.wasm\nexport LST=urn:component:5826760e-216b-4341-a59a-cf3c177743d1\ngolem-cloud-cli component add --project $PRJ --component-name archive precompiled/archive.wasm\nexport ARCHIVE_ID=ec9715ba-feb1-4444-84c5-9ba9f9182dbd\n```\n\nCreate a new list:\n\n```zsh\ngolem-cloud-cli worker start --component $LST --worker-name test --env \"ARCHIVE_COMPONENT_ID=$ARCHIVE_ID\" --env \"EMAIL_COMPONENT_ID=$EMAIL_ID\"\n```\n\nDo some invocations:\n\n```zsh\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test --function 'demo:lst/api.{connect}' --arg '\"vigoo@golem.cloud\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test --function 'demo:lst/api.{add}' --arg '{id: 1}' --arg '\"item 1\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test --function 'demo:lst/api.{add}' --arg '{id: 1}' --arg '\"item 3\"'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test --function 'demo:lst/api.{get}'\ngolem-cloud-cli worker invoke-and-await --component $LST --worker-name test --function 'demo:lst/api.{archive}'\n```\n\nShow how we can check the worker's oplog:\n\n```zsh\ngolem-cloud-cli worker oplog --component $LST --worker-name test\n```\n\nTry only listing entries related to calling exported functions:\n\n```zsh\ngolem-cloud-cli worker oplog --component $LST --worker-name test --query 'exported-function'\n```\n\nExplain that this is a lucid query and can filter on many things.\n\nLook for RPC calls:\n\n```zsh\ngolem-cloud-cli worker oplog --component $LST --worker-name test --query 'rpc'\n```\n\nCheck the other workers:\n\n```zsh\ngolem-cloud-cli worker oplog --component-name email --project $PRJ --worker-name test\ngolem-cloud-cli worker oplog --component-name archive --project $PRJ --worker-name archive\n```\n\nShow how the idempotency key can be used in the query to match specific internal calls.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgolemcloud%2Fgolem-launch-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgolemcloud%2Fgolem-launch-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgolemcloud%2Fgolem-launch-demo/lists"}