{"id":41830428,"url":"https://github.com/cafjs/caf_sharing","last_synced_at":"2026-01-25T08:36:39.666Z","repository":{"id":6333984,"uuid":"7569548","full_name":"cafjs/caf_sharing","owner":"cafjs","description":"CAF library to implement Sharing Actors","archived":false,"fork":false,"pushed_at":"2023-04-18T22:34:22.000Z","size":149,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-04T06:54:29.523Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/cafjs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE-2.0.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-01-12T00:22:10.000Z","updated_at":"2021-11-12T04:08:38.000Z","dependencies_parsed_at":"2023-01-13T13:56:46.872Z","dependency_job_id":null,"html_url":"https://github.com/cafjs/caf_sharing","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cafjs/caf_sharing","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cafjs%2Fcaf_sharing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cafjs%2Fcaf_sharing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cafjs%2Fcaf_sharing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cafjs%2Fcaf_sharing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cafjs","download_url":"https://codeload.github.com/cafjs/caf_sharing/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cafjs%2Fcaf_sharing/sbom","scorecard":{"id":261784,"data":{"date":"2025-08-11","repo":{"name":"github.com/cafjs/caf_sharing","commit":"a8d8fa1519492907bd1dc47d66b3f355b733a3cc"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/push.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/push.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/cafjs/caf_sharing/push.yml/master?enable=pin","Info:   0 out of   1 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":9,"reason":"license file detected","details":["Info: project has a license file: LICENSE-2.0.txt:0","Warn: project license file does not contain an FSF or OSI license."],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-17T10:58:44.564Z","repository_id":6333984,"created_at":"2025-08-17T10:58:44.565Z","updated_at":"2025-08-17T10:58:44.565Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28749604,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-25T08:31:04.260Z","status":"ssl_error","status_checked_at":"2026-01-25T08:30:28.859Z","response_time":113,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":"2026-01-25T08:36:38.673Z","updated_at":"2026-01-25T08:36:39.659Z","avatar_url":"https://github.com/cafjs.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Caf.js\n\nCo-design cloud assistants with your web app and IoT devices.\n\nSee https://www.cafjs.com\n\n## Library for Implementing Sharing Actors\n\n[![Build Status](https://github.com/cafjs/caf_sharing/actions/workflows/push.yml/badge.svg)](https://github.com/cafjs/caf_sharing/actions/workflows/push.yml)\n\n\n\nThis repository implements distributed, replicated data structures for *Sharing Actors*, a core abstraction for our CAs.\n\nIn a traditional Actor model, Actor's state is private, and data sharing is implemented by exchanging messages. This simplifies concurrent programming by avoiding data races, deadlocks, and complex fault recovery.\n\nUnfortunately, when slow changing data has to be shared by many Actors, the most efficient solutions use shared memory, and that breaks the Actor model.\n\nDoes it? Can we create a system that from the outside looks like a duck, walks like a duck, but it combines a shared data structure with an Actor model, in a way that we cannot tell it is not a pure Actor model?\n\nIn the general case we can't. The shared data structure is seen as internal state by many Actors. Changes by one of them could be visible by others in the middle of processing a message, breaking message serialization.\n\nBut if we make certain assumptions we can!\n\n1. *Single Writer*: one Actor *owns* the data structure, the others can only read it. Everybody sees the data structure as internal state.\n\n2. *Readers Isolation*: a read-only view of the data structure can only change between messages.\n\n3. *Fairness*: an Actor cannot indefinitely block other local Actors from seeing new updates.\n\n4. *Writer Atomicity*: changes are flushed, as an atomic unit, when the processing of a message finishes. No partial data leaks allowed.\n\n5. *Consistency*: implements monotonic read consistency, i.e., replicas can be stale, but they never roll back to older versions.\n\nAnd these properties are not that difficult to guarantee in `Caf.js`.\n\nIn `Caf.js` a CA ({@link external:caf_ca}) is an Actor, and an example of a shared data structure is a *SharedMap* ({@link module:caf_sharing/SharedMap}).\n\nWe name a *SharedMap*  with a local name in the context of the CA that owns it, and this makes it trivial to enforce *Single Writer*.\n\n`Caf.js` processes a message within a transaction, and changes to a *SharedMap* are also part of that transaction, guaranteeing *Writer Atomicity*.\n\nMonotonic read consistency is enforced by using version numbers to identify change sets.\n\nThe tricky part is how to guarantee both *Fairness* and *Readers Isolation* at the same time, since they impose conflicting requirements. The solution is to have multiple local versions of a *SharedMap*, and pick the most recent one when processing a new message.  When all the CAs using an old version finish processing its current message, that version gets garbage collected.\n\n*SharedMaps* are implemented with persistent data structures, i.e., `Immutable.js`, to efficiently maintain many read-only snapshots. Since *SharedMaps* can be easily replicated in the browser (and IoT devices.), these persistent data structures are also used by *React/Redux* to speed up user interfaces.\n\n*SharedMaps* can contain serialized methods that `Caf.js` uses to dynamically change the behavior of CAs and IoT devices.. For example, we can hide schema changes by adding getters and setters, or provide new functionality to a device, or change the rules on how CAs react to certain events...\n\nAnd those changes respect  *Single Writer*, *Writer Atomicity*,  *Readers Isolation*, *Fairness*, and *Consistency*, enabling **safe** adaptive behavior.\n\nLet's look at some examples:\n\n### Hello World (see `examples/helloworld`)\n\nEach user has a privileged CA called `admin` that owns a *SharedMap*. All the CAs belonging to this user replicate this map. CAs could be running in different node.js processes, deployed across multiple servers or VMs.\n\nHelper functions to identify the privileged CA, and the name of the *SharedMap*:\n\n```\nconst ADMIN_CA = 'admin';\nconst ADMIN_MAP = 'primarySharedMap';\nconst isAdmin = function(self) {\n    const name = self.__ca_getName__();\n    return (caf.splitName(name)[1] === ADMIN_CA);\n};\nconst primaryMap = function(self) {\n    const name = self.__ca_getName__();\n    return caf.joinName(caf.splitName(name)[0], ADMIN_CA, ADMIN_MAP);\n};\n```\n\nand the CA methods that implement a counter as a *SharedMap* entry:\n\n```\nexports.methods = {\n    async __ca_init__() {\n        if (isAdmin(this)) {\n            this.$.sharing.addWritableMap('primary', ADMIN_MAP);\n        }\n        this.$.sharing.addReadOnlyMap('replica', primaryMap(this));\n        return [];\n    },\n    async increment() {\n        const $$ = this.$.sharing.$;\n        if (isAdmin(this)) {\n            const counter = $$.primary.get('counter') || 0;\n            $$.primary.set('counter', counter + 1);\n            return [null, counter];\n        } else {\n            return [new Error('Cannot write to SharedMap')];\n        }\n    },\n    async getCounter() {\n        const $$ = this.$.sharing.$;\n        return [null, $$.replica.get('counter')];\n    }\n};\n```\n\n### Hello Adaptive (see `examples/helloadaptive`)\n\nLet's add dynamic behavior to the previous example.\n\nThe privileged CA installs in the *SharedMap* a serialized method `computeLabel()` that generates a random label. The method is created with `setFun()` and invoked with `applyMethod()`. Methods can take external arguments (`prefix`), or read map values (`base`).\n\n```\nexports.methods = {\n    ...\n    async install(base) {\n        const $$ = this.$.sharing.$;\n        if (isAdmin(this)) {\n            $$.primary.set('base', base);\n            const body = \"return prefix + (this.get('base') + Math.random());\";\n            $$.primary.setFun('computeLabel', ['prefix'], body);\n            return [null, base];\n        } else {\n            return [new Error('Cannot write to SharedMap')];\n        }\n    },\n    async getLabel(prefix) {\n        const $$ = this.$.sharing.$;\n        try {\n            return [null, $$.replica.applyMethod('computeLabel', [prefix])];\n        } catch (err) {\n            return [err];\n        }\n    }\n};\n```\n\nChanges to `base` and `computeLabel` are committed in a single transaction, eliminating dangerous transients.\n\nAlso, if inside `getLabel()` we call the method `computeLabel()` multiple times, even with asynchronous control flow between calls, the  *Readers Isolation* property guarantees that the method does not change.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcafjs%2Fcaf_sharing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcafjs%2Fcaf_sharing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcafjs%2Fcaf_sharing/lists"}