{"id":21224644,"url":"https://github.com/kurtschelfthout/humanoid","last_synced_at":"2025-08-29T02:37:00.003Z","repository":{"id":68605546,"uuid":"82735994","full_name":"kurtschelfthout/Humanoid","owner":"kurtschelfthout","description":"Human-readable and memorable identifiers for anything.","archived":false,"fork":false,"pushed_at":"2018-03-01T22:46:27.000Z","size":110,"stargazers_count":6,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-10T19:55:34.173Z","etag":null,"topics":["csharp","dotnet","fsharp","human-readable","human-readable-representations","identifier","rfc1751"],"latest_commit_sha":null,"homepage":"","language":"F#","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/kurtschelfthout.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":"2017-02-21T23:00:45.000Z","updated_at":"2023-10-07T16:20:30.000Z","dependencies_parsed_at":"2023-02-27T05:00:22.387Z","dependency_job_id":null,"html_url":"https://github.com/kurtschelfthout/Humanoid","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/kurtschelfthout/Humanoid","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kurtschelfthout%2FHumanoid","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kurtschelfthout%2FHumanoid/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kurtschelfthout%2FHumanoid/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kurtschelfthout%2FHumanoid/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kurtschelfthout","download_url":"https://codeload.github.com/kurtschelfthout/Humanoid/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kurtschelfthout%2FHumanoid/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272612177,"owners_count":24964388,"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-08-29T02:00:10.610Z","response_time":87,"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":["csharp","dotnet","fsharp","human-readable","human-readable-representations","identifier","rfc1751"],"created_at":"2024-11-20T22:59:28.293Z","updated_at":"2025-08-29T02:36:59.980Z","avatar_url":"https://github.com/kurtschelfthout.png","language":"F#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Humanoid - Human friendly Ids\n\nHumanoid provides a small and simple .NET API to convert identifiers (like integers, guids, strings, ...) into a representation that is more easy to pronounce, shout, remember and generally communicate to humans: a bunch of words.\n\nHumanoid is at its core an implementation of [RFC 1751](https://tools.ietf.org/html/rfc1751), with a few small extensions.\n\n## Quickstart\n\nFirst, we need a simple instance of the ```Memo``` type so it knows which word list to use. I'll use F# here, but the whole API is just one plain old .NET type with a few methods, nothing stops you from using this in any .NET language.\n\n```fsharp\n\u003e let memo = new Memo(WordList.RFC1751)\n```\n\nHere's a phone number:\n\n```fsharp\n\u003e memo.FromUInt64 02035567889UL\nval it : string [] = [|\"a\"; \"a\"; \"a\"; \"uses\"; \"wed\"; \"gale\"|]\n```\n\nThat list of six words from the word list uniquely corresponds to the given integer. So given the six words, we can recover it exactly:\n\n```fsharp\n\u003e memo.ToUInt64 [|\"a\"; \"a\"; \"a\"; \"uses\"; \"wed\"; \"gale\"|]\nval it : uint64 = 2035567889UL\n```\n\nReally any 64 bits corresponds uniquely to a list of six words. Humanoid just consists of a few simple functions to convert between the two representations.\n\nThe six words have a few more bits of information for error checking, so say we mishear:\n\n```fsharp\nmemo.ToUInt64 [|\"a\"; \"a\"; \"a\"; \"use\"; \"wed\"; \"gale\"|]\nSystem.Exception: Checksum failed.\n   at FSI_0003.Humanoid.Memo.ToUInt64(IEnumerable`1 words)\n   at \u003cStartupCode$FSI_0009\u003e.$FSI_0009.main@()\nStopped due to error\n```\n\n## How does it work?\n\nHumanoid, as per RFC 1751, contains a list of 2048, or 2^11, words. In other words, each word encodes 11 bits of information. So to represent 64 bits, we need at least 6 words. Since 6*11 is 66 bits, Humanoid uses the two leftover bits as a checksum to catch errors.\n\nYou can think of the words as a base 2048 number system. (This is only approximately correct because of the checksum, but the intuition is correct) As you can tell from the above, the word 'a' is zero.\n\n## More stuff\n\nRFC 1751 also describes how to encode 128bits as a list of 12 words - just by converting the two 64-bit parts separately. A guid is a good example:\n\n```fsharp\n\u003e memo.FromGuid \u003c| Guid.Parse(\"a4133885-613b-40c9-aa4a-94e1c143918f\")\nval it : string [] = [|\"tin\"; \"ally\"; \"atom\"; \"acid\"; \"phi\"; \"tun\"; \"grab\"; \"gave\"; \"tuba\"; \"nell\"; \"web\"; \"been\"|]\n\n\u003e memo.ToGuid [|\"tin\"; \"ally\"; \"atom\"; \"acid\"; \"phi\"; \"tun\"; \"grab\"; \"gave\"; \"tuba\";\"nell\"; \"web\"; \"been\"|]\nval it : Guid = a4133885-613b-40c9-aa4a-94e1c143918f\n```\n\nHumanoid generalizes that further and can convert a sequence of 64 bits to a list of words and back, using ```Memo.FromUInt64s``` and ```Memo.ToUInt64s```.\n\nIf you have a small number, relative to the range of a UInt64, the leading words will be 'a' because that's the 0th word in the wordlist. Humanoid can omit leading 'a's, similar to omitting leading zeroes:\n\n```fsharp\n\u003e memo.FromUInt64Short 6587UL\nval it : string [] = [|\"alp\"; \"skim\"|]\n\n\u003e memo.ToUInt64Short [ \"alp\"; \"skim\" ]\nval it : uint64 = 6587UL\n```\n\nYou may find 'alp skim' or the like easier to remember than your pin code.\n\nFinally, there is also a ```WordList.Frequent``` with the 2048 most frequently used words from a British English corpus. (Obviously depending on the corpus, the most frequently used words will be different.)\n\n```fsharp\n\u003e let memo = Memo(WordList.Frequent)\n\u003e memo.FromGuid \u003c| Guid.Parse(\"a4133885-613b-40c9-aa4a-94e1c143918f\")\nval it : string [] =\n  [|\"grow\"; \"kill\"; \"manage\"; \"top\"; \"team\"; \"union\"; \"distance\"; \"target\";\n    \"yellow\"; \"combination\"; \"herself\"; \"principle\"|]\n```\n\n## Why don't you tell me what you really think?\n\nThis is a cute idea, and there is some potential here. Obviously to communicate an identifier, say in a situation where you're troubleshooting a production bug that manifests itself when someone tries to edit an entity with a particular id, it's much easier for both you and the other person to communicate by copy-pasting the id in a chat or email.\n\nBut I've been in situations where - esp. where only email is available - this breaks the flow of communication because sending and receiving an email can take some time. Probably longer than for one person to say six short words, and for you to type them in. Also some normal people (I mean non-programmers...) like to send screenshots when they encounter a problem, and in that case copy-pasting is not an option. I'd rather type 12 short words than a guid.\n\nUsing words leverages dictionary-based completion, say you're typing an identifier on a mobile device.\n\nSome identifiers (guids, SHAs) are so atrocious that people have taken to talk about them by saying the first few characters. I've done this sometimes when talking to someone about a few commits. I'd rather talk about commits 'alp' and 'bean' than 'c4gfe' and 'ffb4e'.\n\nHow many times have you had to say your credit card number, account number and so on over the phone? Wouldn't you rather have said a few short words instead?\n\nThat said, I don't find RFC 1751 entirely satsifactory. In particular, the choice of word list is not great - they are all 1-4 letter words and thus short, but shortness in this case is not necessarily a virtue. For example, 'bean', 'been' and 'beam' are all in there, and those are impossible or hard to distinguish by ear. Also it makes them harder to recall if you don't have a strong visual memory. Hence the alternative list of 2048 frequently used words (no matter the length) is probably better.\n\nEven more memorable would be to form simple sentences given lists of nouns, verbs and adjectives: 'The quick fox jumps over the lazy dog' is also 6 words (2 adjectives, 1 verb, 1 preposition, 2 nouns) and is a lot more memorable than 6 somewhat random words, even frequently used ones.\n\nFinally, to quote Nelson Mandela:\n\n\u003e If you talk to a man in a language he understands, that goes to his head. If you talk to him in his language, that goes to his heart.\n\nSo to be human friendly, the words should be in whatever language the user is most comfortable in. Luckily, you can plug in whatever word list you need - there just need to be exactly 2048 distinct words.\n\n## Ok, but still: Why?\n\nI thought about the problem of human-readable identifiers for a bit, and found out by accident that an RFC exists for exactly that. I found it interesting enough to tinker with. I haven't actually used this for anything real. I have seen something like this used though, e.g. Keybase generates a list of words to be used as a paper public key instead of the usual Base64 encoded gibberish.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkurtschelfthout%2Fhumanoid","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkurtschelfthout%2Fhumanoid","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkurtschelfthout%2Fhumanoid/lists"}