{"id":29856377,"url":"https://github.com/foxssake/trimsock","last_synced_at":"2025-07-29T23:37:06.433Z","repository":{"id":307005319,"uuid":"998512463","full_name":"foxssake/trimsock","owner":"foxssake","description":"A human-readable communication protocol","archived":false,"fork":false,"pushed_at":"2025-07-28T22:06:48.000Z","size":79,"stargazers_count":1,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-29T00:08:05.124Z","etag":null,"topics":["network","protocol"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/foxssake.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,"zenodo":null}},"created_at":"2025-06-08T19:08:05.000Z","updated_at":"2025-07-28T22:06:49.000Z","dependencies_parsed_at":"2025-07-29T00:08:08.995Z","dependency_job_id":"317afab6-ab89-4976-a826-eef9dd29c68a","html_url":"https://github.com/foxssake/trimsock","commit_stats":null,"previous_names":["foxssake/trimsock"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/foxssake/trimsock","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foxssake%2Ftrimsock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foxssake%2Ftrimsock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foxssake%2Ftrimsock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foxssake%2Ftrimsock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/foxssake","download_url":"https://codeload.github.com/foxssake/trimsock/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foxssake%2Ftrimsock/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267780494,"owners_count":24143204,"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-07-29T02:00:12.549Z","response_time":2574,"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":["network","protocol"],"created_at":"2025-07-29T23:36:41.483Z","updated_at":"2025-07-29T23:37:06.405Z","avatar_url":"https://github.com/foxssake.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Trimsock\n\nTrimsock is a stream-based communication protocol that:\n\n* is easy to implement\n* is human-readable\n* supports binary\n* is extended via conventions\n\n## Quick glance\n\nTrimsock uses commands to transmit instructions, e.g.:\n\n```\n\u003e\u003e\u003e login tom@acme.com:ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f\n\u003c\u003c\u003c set-sessionid ZcLpOdmxgQf9\n```\n\nEach command consists of a *name*, a space, *data*, and a terminating newline.\n\nIt can also do *request-response pairs*, by encoding a request ID in the command name:\n\n```\n\u003e\u003e\u003e login?pDYqh3ghn241 tom@acme.com:3dff73672811dcd9f93f3dd86ce4e04960b46e10827a55418c7cc35d596e9662\\n\n\u003e\u003e\u003e !pDYqh3ghn241 Wrong password!\n\u003e\u003e\u003e login?i6QhjOtphK2m tom@acme.com:ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f\\n\n\u003c\u003c\u003c .i6QhjOtphK2m OK\\n\n```\n\nThis way, responses can indicate whether the request was successful or not.\n\nFor responses with many items, Trimsock also supports *streams* in a similar manner:\n\n```\n\u003e\u003e\u003e lobbies?qX42w1PfY9Sq\n\u003c\u003c\u003c |qX42w1PfY9Sq 50UPmO6lk4Uq Cool\\sLobby\n\u003c\u003c\u003c |qX42w1PfY9Sq C7Yfk3UP07Ag Dave's\\sGarage\\sMatches\n\u003c\u003c\u003c |qX42w1PfY9Sq MV1oLTkTPwTS casual\\sgang\n\u003c\u003c\u003c |qX42w1PfY9Sq \n```\n\nThe protocol also supports multiple parameters per command, and raw messages\nfor transmitting binary. Find out more from the\n[Specification](#specification).\n\n## Use case\n\nTrimsock aims to fill the niche of a bi-directional, structured protocol, while\nbeing easy to understand and implement.\n\nThis could mean, among others, adapting a TCP socket for native applications,\nor sitting on top of WebSockets, providing structure.\n\n## Implementations\n\nAnyone is free to implement the trimsock protocol. This repository hosts\nreference implementations in different languages.\n\n- [trimsock.js](./trimsock.js)\n\n## Specification\n\n\u003e [!WARNING]  \n\u003e The specification is still work in progress\n\n### Core\n\nImplementing the core specification is enough for conformance. Additional\nfeatures may be built on top of it as conventions.\n\n#### Commands\n\nTrimsock exchanges *commands*, with each *command* consisting of the *command\nname*, a single space, the *command data* and a single newline character,\ndelimiting the command:\n\n```\n[command name] [command data]\\n\n```\n\nAn example command:\n\n```\nlogin tom@acme.com:ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f\n```\n\nA *command* may omit the *command data*:\n\n```\n[command name] \\n\n```\n\nNote that even without *command data*, the space character is required.\n\n*Commands* MUST be parsed as UTF-8 strings.\n\n#### Escape sequences\n\nBoth the *command name* and *command data* may want to encode characters that\nare otherwise used for the protocol itself, e.g. newlines. In these cases,\nthese special characters are *escaped* based on the following table:\n\n| Character | Character byte | Escape sequence | Escape bytes |\n|-----------|----------------|-----------------|--------------|\n| `\\n`      | `0x0A`         | `\\\\n`           | `0x5C 0x6E`  |\n| `\\r`      | `0x0D`         | `\\\\r`           | `0x5C 0x72`  |\n| ` `       | `0x20`         | `\\\\s`           | `0x5C 0x73`  |\n\nDuring parsing, these escape sequences must be replaced with their original\ncounterparts.\n\nThe space character's escape sequence is only recognized in the *command name*.\nIn command data, the `\\\\s` character sequence is left as-is.\n\n#### Reserved characters in command names\n\nThe conventions described later on use certain characters to encode extra\ninformation in command names. To avoid conflicting with these, implementations\nSHOULD NOT use the following characters in command names, unless implementing a\nconvention that requires it:\n\n- `?`\n- `.`\n- `!`\n- `|`\n- `$`\n\n#### Raw data\n\nIn some cases, it can be beneficial to send larger chunks of data without\nescaping and unescaping the *command data*.\n\nIn these cases, commands with *raw data* may be sent, with the following\nformat:\n\n```\n\\r[command name] [data size in bytes]\\n\n[raw data]\\n\n```\n\nExample:\n\n```\n\\rset-picture 1524\\n\n\\xFF\\xD8\\xFF\\xE1\\x00\\x18\\x45\\x78\\x69\\x66\\x00\\x00\\x49\\x49...\\n\n```\n\nRaw data MUST be interpreted as-is, without any kind of decoding algorithm (\ni.e. don't parse it as UTF-8 ).\n\n### Conventions\n\nConventions build on top of the base specifications. They do so in a way that\nproduces commands still adhering to the base specification. In other words, if\na given implementation does not recognize a given convention, it can still at\nleast parse the convention's commands.\n\n#### Multiple command parameters\n\nIn case a command needs multiple parameters instead of a single *command data*\nblob, implementations may split the *command data* into multiple *command\nparameters*.\n\nThis is done by splitting the *command data* at every space character:\n\n```\n[command name] [command parameter] [command parameter] [...]\\n\n```\n\nFor example:\n\n```\nset-user-details Tom Acme tom@acme.com\n```\n\nFor this conventions, implementations MUST recognize the following escape\nsequence:\n\n| Character | Escape sequence | Byte sequence (hexadecimal) |\n|-----------|-----------------|-----------------------------|\n| ` `       | `\\\\s`           | `0x5C 0x73`                 |\n\nThis allows *command parameters* to contain space characters.\n\nConforming implementations MUST NOT parse *raw command data* as multiple\nparameters.\n\n#### Request-response pairs\n\nA common use case is requesting some data, and then receiving it in a response\nmessage.\n\nTo support this, *command names* may be extended with a *request id* that\nuniquely identifies the request-response exchange:\n\n```\n[command name]?[request-id] [command data]\\n\n```\n\nThis initiates a request with the given ID. Later commands can use this ID to\nuniquely identify the request they're responding to.\n\nNote that to an implementation that does not handle request-response pairs,\nthis is still a valid command.\n\nThe *request ID* must be a string unique to the connection. Implementations are\nfree to choose their own approach, for example sequential IDs, [UUIDs], or\n[nanoids].\n\nOnce a request is received, a response MUST be sent with the same request ID.\nIf the request was successfully processed, send a *success response* in the\nfollowing form:\n\n```\n.[request-id] [command data]\\n\n```\n\nIf the request cannot be processed, send an *error response*:\n\n```\n![request-id] [command data]\\n\n```\n\nThe contents of the *command data* are entirely up to the application - among\nothers, it can be used to indicate why the request wasn't processed.\n\nFor every request, only a single response MUST be sent. Further responses MUST NOT\nbe considered. Implementations MAY discard further responses, or they MAY raise\nan error. Implementations MAY reuse request ID's, as long as that causes no\nambiguity.\n\nAn example request-response flow:\n\n```\n\u003e\u003e\u003e login?pDYqh3ghn241 tom@acme.com:3dff73672811dcd9f93f3dd86ce4e04960b46e10827a55418c7cc35d596e9662\\n\n\u003e\u003e\u003e !pDYqh3ghn241 Wrong password!\n\u003e\u003e\u003e login?i6QhjOtphK2m tom@acme.com:ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f\\n\n\u003c\u003c\u003c .i6QhjOtphK2m OK\\n\n```\n\nThe response commands SHOULD NOT contain the original command name.\nImplementations are free to ignore the command name, as they only rely on the\nrequest ID. Which means that this is a valid exchange as well:\n\n```\n\u003e\u003e\u003e login?pDYqh3ghn241 tom@acme.com:3dff73672811dcd9f93f3dd86ce4e04960b46e10827a55418c7cc35d596e9662\\n\n\u003e\u003e\u003e login!pDYqh3ghn241 Wrong password!\n\u003e\u003e\u003e login?i6QhjOtphK2m tom@acme.com:ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f\\n\n\u003c\u003c\u003c login.i6QhjOtphK2m OK\\n\n```\n\n#### Streaming\n\nLarge amounts of data can be impractical to transmit in one large *command*.\nInstead, multiple commands are sent in order, each with a chunk of the full\ndata.\n\nStreams may be initiated by the sender, or sent in response to a request.\n\nWhen initiated by the sender, the first message MUST denote the *command name*\nand the *stream ID*, along with the first data chunk:\n\n```\n[command name]|[stream-id] [data chunk]\\n\n```\n\nIf the stream is initiated in response to a request, the request ID MUST be\nused as the stream ID. The command name MUST NOT be included:\n\n```\n|[stream-id] [data chunk]\\n\n```\n\nThis first command is also used to transmit the first chunk of data.\n\nFrom here, each data chunk is sent in the same format:\n\n```\n|[stream-id] [data chunk]\\n\n```\n\nOnce all the data has been sent, the stream MUST be terminated with an empty\ndata chunk:\n\n```\n|[stream-id] \\n\n```\n\nAn example stream exchange, combined with request-response pairs:\n\n```\n\u003e\u003e\u003e get-file|AUygn0OwMgYu big-video.mp4\n\u003c\u003c\u003c |AUygn0OwMgYu \\b1024...\\n\n\u003c\u003c\u003c |AUygn0OwMgYu \\b1024...\\n\n\u003c\u003c\u003c |AUygn0OwMgYu \\b1024...\\n\n\u003c\u003c\u003c |AUygn0OwMgYu \\n\n```\n\n\u003e[!NOTE]\n\u003e While the [Request-response pairs] convention specifies that only one\n\u003e *response command* can be sent for a request, *stream commands* are exempt from\n\u003e this rule, with the virtue of being a different kind of command.\n\n\n[C escape sequences]: https://en.wikipedia.org/wiki/Escape_sequences_in_C#Escape_sequences\n[UUIDs]: https://en.wikipedia.org/wiki/Universally_unique_identifier\n[nanoids]: https://github.com/ai/nanoid\n[Request-response pairs]: #request-response-pairs\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoxssake%2Ftrimsock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffoxssake%2Ftrimsock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoxssake%2Ftrimsock/lists"}