{"id":28396272,"url":"https://github.com/ssbc/ssb-validate","last_synced_at":"2025-09-02T13:13:02.793Z","repository":{"id":52230635,"uuid":"87892940","full_name":"ssbc/ssb-validate","owner":"ssbc","description":"better ssb validator","archived":false,"fork":false,"pushed_at":"2021-05-04T09:18:12.000Z","size":122,"stargazers_count":12,"open_issues_count":1,"forks_count":8,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-08-21T06:56:08.657Z","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":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ssbc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-11T05:38:57.000Z","updated_at":"2022-05-16T09:04:07.000Z","dependencies_parsed_at":"2022-09-11T10:04:31.651Z","dependency_job_id":null,"html_url":"https://github.com/ssbc/ssb-validate","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/ssbc/ssb-validate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssbc%2Fssb-validate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssbc%2Fssb-validate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssbc%2Fssb-validate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssbc%2Fssb-validate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ssbc","download_url":"https://codeload.github.com/ssbc/ssb-validate/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssbc%2Fssb-validate/sbom","scorecard":{"id":844525,"data":{"date":"2025-08-11","repo":{"name":"github.com/ssbc/ssb-validate","commit":"36a4104d898ee2c341745f459bbc053ee2b36f62"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.2,"checks":[{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"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":"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":"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":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"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":"Dangerous-Workflow","score":-1,"reason":"no workflows found","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":"Code-Review","score":5,"reason":"Found 8/16 approved changesets -- score normalized to 5","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":"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":"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":"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":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"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":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"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"}},{"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":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 23 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-23T21:08:46.774Z","repository_id":52230635,"created_at":"2025-08-23T21:08:46.774Z","updated_at":"2025-08-23T21:08:46.774Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273288234,"owners_count":25078687,"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","status":"online","status_checked_at":"2025-09-02T02:00:09.530Z","response_time":77,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":"2025-05-31T21:37:47.939Z","updated_at":"2025-09-02T13:13:02.786Z","avatar_url":"https://github.com/ssbc.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ssb-validate\n\nvalidate ssb messages, completely functionally.\n\nthis module seeks to replace ssb-feed, and contain the logic to validate ssb messages,\nbut this is implemented functionally, with serializable state.\nthis means, that the states generated when the reference implementation runs\ncan be extracted and used as test cases in other implementations.\n\n## example\n\n``` js\nvar validate = require('ssb-validate')\nvar hmac_key = null\nvar state = validate.initial()\n\nvar msgs = [...] //some source of messages\n\nmsgs.forEach(function (msg) {\n  try {\n    state = validate.append(state, hmac_key, msg)\n  } catch (err) {\n    console.log(err)\n  }\n})\n\nwriteToDatabase(state.queue, function (err) {\n  if(err) throw err\n\n  //these messages are fully accepted now, can remove them from state.\n  state.queue = []\n})\n\n//state should be saved in some way it can be reconstructed\n//so in the future it can be appended to starting from scratch.\n```\n\n## architecture\n\nThis module describes the validation logic for ssb messages, using a reduce style\npattern - `state = method(state, argument)` . To make it easy to test, the various\nsubcomponents are also exported, but the main method is `state = append(state, hmac_key, msg)`\n\n## state\n\nthis module uses a data structure to represent the state of validation.\nThe structure is as follows:\n\n``` js\n{\n  validated: integer,\n  queued: integer,\n  queue: [kvt...], //messages which have been validated and may now be written to database\n  feeds: { //validation state of all feeds\n    \u003cfeed_id\u003e: {\n      id: \u003cmsgId: key of last message\u003e,\n      timestamp: \u003ctimestamp of last message\u003e,\n      sequence: integer //\n      queue: [kvt...]\n    },...\n  }\n}\n```\nnote: `kvt` here represents a msg with key calculated and a recieved timestamp assigned\n`{key: hash(msg), value: msg, timestamp: timestamp()}`\n\n## api\n\n### state = validate.append(state, hmac_key, msg)\n\nAppend and validate a message to state. If the message is valid, it will appear at the\nend of `state.queue` it may now be written to the database.\n\n`hmac_key` is optional - if provided, messages are hmac'd with it before signing,\nwhich can be used to create a separate network in which messages cannot be copied\nto the main network. Messages compatible with the main network have `hmac_key = null`\n\n### msg = validate.create(feed_state, keys, hmac_key, content, timestamp)\n\nCreate a message that is valid given the current state of the feed (such that it may be passed to \n`append(state, hmac_key, msg)`. `keys` is the signing key, as provided by `ssbKeys.generate()` or `ssbKeys.createOrLoadSync(filename)`\n\n### msg_id = validate.id(msg)\n\nCalculate the message id for a given message.\n\n## shortcuts for js mode api\n\nIn investigating the possiblity of an entirely web based scuttlebutt,\nverifying all signatures in javascript had added a lot of overhead.\n(although now this is not such a problem because of crypto in fast-enough webassembly)\n\nThe following methods queues some number of messages and then validates the last signature.\nIt is possible that a specially constructed messages with some invalid signatures, followed\nby messages with valid signatures could get accepted, but the writer doesn't have any way\nto know _which_ messages will be accepted, and a 3rd party could not insert invalid messages\ninto another feed, because the signature that is eventually checked wouldn't point to the right\nprevious hash.\n\nHowever, you can probably consider these methods not necessary. And they could be removed.\n\n\n```\nvar validate = require('ssb-validate')\nvar hmac_key = null\nvar state = validate.initial()\n\nvar msgs = [...] //some source of messages\n\n//queue messages\nmsgs.forEach(function (msg) {\n  state = validate.queue(state, msg)\n  if(state.error) console.error(state.error)\n})\n\n//validate messages\n\nfor(var feed_id in state.feeds)\n  state = validate.validate(state, hmac_key, feed_id)\n\nwriteToDatabase(state.queue, function (err) {\n  if(err) throw err\n\n  //these messages are fully accepted now, can remove them from state.\n  state.queue = []\n})\n\n//state should be saved in some way it can be reconstructed\n//so in the future it can be appended to starting from scratch.\n```\n\n### state = validate.queue(state, msg)\n\nCall checkInvalidCheap and if valid, append to the feed's incoming queue.\n(`state.feeds[id(msg)].queue`)\n\nThe message is checked to have an incrementing `sequence`, and correct `previous` hash,\nbut signature is not checked. (the intention here is for when using javascript crypto,\nchecking every signature is expensive) however, this is not a big problem with wasm crypto.\n\n### state = validate.validate(state, feed_id)\n\nCheck the signature of the last message in feed_id's incoming queue,\nand if it is valid, append all messages in that queue.\nAs optimization/shortcut for javascript crypto.\n\n### state = validate.appendOOO(state, hmac_key, msg)\n\nThis method works the same as append except that it does not check the\nprevious link of the message. This allows one to validate an out of\norder message or a row of messages not starting from the beginning.\n\n## internal api\n\nThe following methods are exposed for testing, but are unlikely to be used directly.\n\n### state = validate.appendNew(state,hmac_key, keys, content, timestamp)\n\nWrapper around create and append. used in testing.\n\n### state = validate.appendKVT (state, hmac_key, kvt)\n\nInternal details of append - recently this was refactored to avoid calculating\nthe message id twice.\n\n### isInvalid = validate.checkInvalidCheap (state, msg)\n\nPerform cheap checks for message validity, but not the signature.\nreturn false if the message is valid, and an error (with message) if it's invalid.\n\n### isInvalid = validate.checkInvalid (state, hmac_key, msg)\n\nCheck signature, returns false if message is valid. returns an error (with message)\nif the message is invalid.\n\n## License\n\nMIT\n\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fssbc%2Fssb-validate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fssbc%2Fssb-validate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fssbc%2Fssb-validate/lists"}