{"id":13725181,"url":"https://github.com/qntm/base2048","last_synced_at":"2026-02-19T20:04:17.231Z","repository":{"id":27041284,"uuid":"105925721","full_name":"qntm/base2048","owner":"qntm","description":"Binary encoding optimised for Twitter","archived":false,"fork":false,"pushed_at":"2026-01-01T14:49:54.000Z","size":2338,"stargazers_count":869,"open_issues_count":0,"forks_count":20,"subscribers_count":8,"default_branch":"main","last_synced_at":"2026-01-04T21:57:54.351Z","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/qntm.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2017-10-05T18:30:23.000Z","updated_at":"2026-01-01T14:49:57.000Z","dependencies_parsed_at":"2023-01-14T09:15:23.239Z","dependency_job_id":"5add45db-6883-4189-8602-8650ce11ba82","html_url":"https://github.com/qntm/base2048","commit_stats":{"total_commits":65,"total_committers":4,"mean_commits":16.25,"dds":"0.36923076923076925","last_synced_commit":"cc64e0843a5e6c54035ddfc7b1077db4706ad5dd"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/qntm/base2048","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qntm%2Fbase2048","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qntm%2Fbase2048/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qntm%2Fbase2048/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qntm%2Fbase2048/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qntm","download_url":"https://codeload.github.com/qntm/base2048/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qntm%2Fbase2048/sbom","scorecard":{"id":754258,"data":{"date":"2025-08-11","repo":{"name":"github.com/qntm/base2048","commit":"76b9857798fcc251395cdddeb1becf7bb0df2752"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.5,"checks":[{"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/workflow-1.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":"Maintained","score":0,"reason":"1 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":"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":"Code-Review","score":0,"reason":"Found 0/13 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":"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":"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":"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/workflow-1.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/qntm/base2048/workflow-1.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/workflow-1.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/qntm/base2048/workflow-1.yml/main?enable=pin","Warn: npmCommand not pinned by hash: .github/workflows/workflow-1.yml:26","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 npmCommand 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":"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":"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":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: MIT License: LICENSE.txt: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":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 21 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"}},{"name":"Vulnerabilities","score":7,"reason":"3 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-22T21:09:29.155Z","repository_id":27041284,"created_at":"2025-08-22T21:09:29.161Z","updated_at":"2025-08-22T21:09:29.161Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29629708,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-19T18:02:07.722Z","status":"ssl_error","status_checked_at":"2026-02-19T18:01:46.144Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6: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":"2024-08-03T01:02:15.148Z","updated_at":"2026-02-19T20:04:17.198Z","avatar_url":"https://github.com/qntm.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# base2048\n\nBase2048 is a binary encoding optimised for transmitting data through Twitter. This JavaScript module, `base2048`, is the first implementation of this encoding. Using Base2048, up to 385 octets can fit in a single Tweet. Compare with [Base65536](https://github.com/qntm/base65536), which manages only 280 octets.\n\n\u003ctable\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n      \u003cth colspan=\"2\" rowspan=\"2\"\u003eEncoding\u003c/th\u003e\n      \u003cth colspan=\"3\"\u003eEfficiency\u003c/th\u003e\n      \u003cth rowspan=\"2\"\u003eBytes per Tweet *\u003c/th\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003eUTF\u0026#x2011;8\u003c/th\u003e\n      \u003cth\u003eUTF\u0026#x2011;16\u003c/th\u003e\n      \u003cth\u003eUTF\u0026#x2011;32\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd rowspan=\"5\"\u003eASCII\u0026#x2011;constrained\u003c/td\u003e\n      \u003ctd\u003eUnary / \u003ca href=\"https://github.com/ferno/base1\"\u003eBase1\u003c/a\u003e\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e0%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e0%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e0%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e1\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eBinary\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e13%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e6%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e3%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e35\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eHexadecimal\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e50%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e25%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e13%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e140\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eBase64\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e\u003cstrong\u003e75%\u003c/strong\u003e\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e38%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e19%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e210\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003eBase85 †\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e80%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e40%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e20%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e224\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd rowspan=\"4\"\u003eBMP\u0026#x2011;constrained\u003c/td\u003e\n      \u003ctd\u003e\u003ca href=\"https://github.com/ferno/hexagram-encode\"\u003eHexagramEncode\u003c/a\u003e\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e25%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e38%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e19%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e105\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\u003ca href=\"https://github.com/ferno/braille-encode\"\u003eBrailleEncode\u003c/a\u003e\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e33%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e50%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e25%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e140\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\u003ca href=\"https://github.com/qntm/base2048\"\u003eBase2048\u003c/a\u003e\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e56%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e69%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e34%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e\u003cstrong\u003e385\u003c/strong\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\u003ca href=\"https://github.com/ferno/base32768\"\u003eBase32768\u003c/a\u003e\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e63%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e\u003cstrong\u003e94%\u003c/strong\u003e\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e47%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e263\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd rowspan=\"3\"\u003eFull Unicode\u003c/td\u003e\n      \u003ctd\u003e\u003ca href=\"https://github.com/keith-turner/ecoji\"\u003eEcoji\u003c/a\u003e\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e31%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e31%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e31%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e175\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\u003ca href=\"https://github.com/ferno/base65536\"\u003eBase65536\u003c/a\u003e\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e56%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e64%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e\u003cstrong\u003e50%\u003c/strong\u003e\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e280\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\u003ca href=\"https://github.com/ferno/base131072\"\u003eBase131072\u003c/a\u003e ‡\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e53%+\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e53%+\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e53%\u003c/td\u003e\n      \u003ctd style=\"text-align: right;\"\u003e297\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n\\* A Tweet can be up to 280 Unicode characters, give or take Twitter's complex \"weighting\" calculation.\u003cbr/\u003e\n† Base85 is listed for completeness but all variants use characters which are considered hazardous for general use in text: escape characters, brackets, punctuation *etc.*.\u003cbr/\u003e\n‡ Base131072 is a work in progress, not yet ready for general use.\u003cbr/\u003e\n\n## Installation\n\n```bash\nnpm install base2048\n```\n\n## Usage\n\n```js\nimport { encode, decode } from 'base2048'\n\nconst uint8Array = new Uint8Array([1, 2, 4, 8, 16, 32, 64, 128])\nconst str = encode(uint8Array)\nconsole.log(str) // 'GƸOʜeҩ'\n\nconst uint8Array2 = decode(str)\nconsole.log(uint8Array2)\n// [1, 2, 4, 8, 16, 32, 64, 128]\n```\n\n## API\n\n`base2048` accepts and returns `Uint8Array`s. Note that every Node.js `Buffer` is a `Uint8Array`. A `Uint8Array` can be converted to a Node.js `Buffer` like so:\n\n```js\nconst buffer = Buffer.from(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength)\n```\n\n### encode(uint8Array)\n\nEncodes a [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) and returns a Base2048 `String` suitable for passing through Twitter. Give or take some padding characters, the output string has 1 character per 11 bits of input.\n\n### decode(string)\n\nDecodes a Base2048 `String` and returns a `Uint8Array` containing the original binary data.\n\n## Rationale\n\nOriginally, Twitter allowed Tweets to be at most 140 characters. Discounting URLs, which have their own complex rules, Tweet length was computed as the number of Unicode code points in the Tweet — *not* the number of octets in any particular encoding of that Unicode string. In 2015, observing that most existing text-based encodings made negligible use of most of the Unicode code point space (e.g. Base64 encodes only 6 bits per character = 105 octets per Tweet), I developed [Base65536](https://github.com/qntm/base65536), which encodes 16 bits per character = 280 octets per Tweet.\n\nOn 26 September 2017, Twitter \u003ca href=\"https://blog.twitter.com/official/en_us/topics/product/2017/Giving-you-more-characters-to-express-yourself.html\"\u003eannounced that\u003c/a\u003e\n\n\u003e we're going to try out a longer limit, 280 characters, in languages impacted by cramming (which is all except Japanese, Chinese, and Korean).\n\nThis statement is fairly light on usable details and/or factual accuracy. However, following some experimentation and examination of the new web client code, we now understand that maximum Tweet length is indeed 280 Unicode code points, *except that code points U+1100 HANGUL CHOSEONG KIYEOK upwards now count double*.\n\nEffectively, Twitter divides Unicode into 4,352 \"light\" code points (U+0000 to U+10FF inclusive) and 1,109,760 \"heavy\" code points (U+1100 to U+10FFFF inclusive).\n\nBase65536 *solely* uses heavy characters, which means that a new \"long\" Tweet can still only contain at most 140 characters of Base65536, encoding 280 octets. This seemed like an imperfect state of affairs to me, and so here we are.\n\nBase2048 solely uses light characters, which means a new \"long\" Tweet can contain at most 280 characters of Base2048. Base2048 is an 11-bit encoding, so those 280 characters encode 3080 bits i.e. 385 octets of data, significantly better than Base65536.\n\n### Note\n\nAt the time of writing, the sophisticated weighted-code point check is only carried out client-side. Server-side, the check is still a simple code point length check, now capped at 280 code points. So, by circumventing the client-side check, it's possible to send \u003ca href=\"https://twitter.com/dx_test1/status/912835316679151621\"\u003e280 characters of Base65536 i.e. 560 bytes of data in a single Tweet\u003c/a\u003e.\n\nBase2048 was developed under the assumption that most people will not go to the trouble of circumventing the client-side check and/or that eventually the check will be implemented server-side as well.\n\n## Code point safety\n\nBase2048 uses only [\"safe\" Unicode code points](https://qntm.org/safe) (no unassigned code points, no control characters, no whitespace, no combining diacritics, ...). This guarantees that the data sent will remain intact when sent through any \"Unicode-clean\" text interface.\n\nIn the available space of 4,352 light code points, there are 2,343 safe code points. For Base2048, since I felt it improved the character repertoire, I further ruled out the four \"Symbol\" General Categories, leaving 2,212 safe code points, and the \"Letter, Modifier\" General Category, leaving 2,176 safe code points. From these I chose 2\u003csup\u003e11\u003c/sup\u003e = 2048 code points for the primary repertoire and 2\u003csup\u003e3\u003c/sup\u003e = 8 additional code points to use as padding characters.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqntm%2Fbase2048","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqntm%2Fbase2048","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqntm%2Fbase2048/lists"}