{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreicp45uyf5zb7wo3uz33qsyy7abuphjcqrkgdnixm7zp5bykc7c2di",
"uri": "at://did:plc:iw5jw32bqqdf4bkbhotvf23m/app.bsky.feed.post/3mneu5t2ugux2"
},
"description": "Technical-narrative document | soledade.city | port 1915\n\n\n\nSOLED/2\n\n\n\nThe Soledade publishing protocol\n\n\nPlain text over TCP for citizenship, Markdown, and editorial autonomy\n\n\n\n\nExecutive summary\n\n\nSOLED/2 is a plain-text application protocol that runs over TCP on port 1915. It was created so that citizens of Soledade can register an identity, publish Markdown, validate drafts, list their files, delete content, and recover access without depending on a web dashboard, REST API, WordPress, or a ",
"path": "/soled-2-the-soledade-publishing-protocol/",
"publishedAt": "2026-06-03T10:02:43.000Z",
"site": "https://pablomurad.com",
"textContent": "**Technical-narrative document | soledade.city | port 1915**\n\n## SOLED/2\n\n### The Soledade publishing protocol\n\n**Plain text over TCP for citizenship, Markdown, and editorial autonomy**\n\n* * *\n\n## Executive summary\n\n**SOLED/2** is a plain-text application protocol that runs over TCP on port **1915**. It was created so that citizens of Soledade can register an identity, publish Markdown, validate drafts, list their files, delete content, and recover access without depending on a web dashboard, REST API, WordPress, or a relational database.\n\nThis document presents SOLED/2 as a deliberate architectural component: simple enough to fit inside a terminal, strict enough to preserve operational safety, and symbolic enough to turn remote publishing into an act of citizenship inside **soledade.city**.\n\n* * *\n\n## 1. What SOLED/2 is\n\nSOLED/2 is the official write contract between simple clients — such as `ncat`, automation scripts, and terminals — and the `soledade.post_server` server.\n\nIt does not try to be an IETF standard. It is not a public RFC. It does not compete with HTTP. Its role is narrower and more honest: it transports commands and Markdown into Soledade's `content/` tree, where the static site is rebuilt after validation.\n\nThe name **SOLED** comes from **Soledade**. The `/2` suffix marks the second generation of the posting protocol. Before it, there was only the legacy mode: an administrative token, a file path, and a body terminated by a single dot. That worked, but only for a city governed by one key.\n\n* * *\n\n## 2. Why it was created\n\nThe original problem was simple: publish remotely without a web dashboard.\n\nFor a single administrator, a secret token was enough. But Soledade stopped being a personal blog and started adopting a more ambitious metaphor: a textual city with citizens, houses, districts, documents, and rules of coexistence.\n\nIn that new scenario, a single shared token became both a bottleneck and a risk. It does not separate authorship. It does not create individual identity. It does not provide a solid basis for moderation. Worst of all, it turns every authorized client into something too powerful.\n\nBluntly: a shared secret is acceptable for personal automation; it is bad architecture for a community.\n\nSOLED/2 was created to solve that scaling problem without betraying the philosophy of the project. The answer was not to add a dashboard, a heavy web stack, or a JSON API. The answer was to design a small, textual, explicit protocol in which each operation says who is speaking, what they want to do, and which file they are acting on.\n\n* * *\n\n## 3. The real need: citizenship, not just login\n\nThe protocol had to solve something larger than authentication. It had to allow entry into Soledade's citizenship model.\n\nThat means allowing a person to:\n\n * register a handle;\n * receive a secret identity;\n * write only inside their own file territory;\n * validate drafts before publication;\n * list their own Markdown files;\n * delete authorized content;\n * recover access if their identity is lost.\n\n\n\nThe choice of plain text is part of the need itself. Anyone with `ncat` can understand, assemble, and send a request. The packet is readable. The error is readable. The response is readable. That reduces dependence on SDKs, libraries, or official clients. The city accepts humble tools.\n\nBut simplicity is not naivety. SOLED/2 defines boundaries: allowed paths, citizen status, maximum payload size, preview without writing, recovery with invalidation of the previous identity, IP-based rate limiting, and synchronized build execution to keep the site coherent.\n\n* * *\n\n## 4. Design principles\n\nPrinciple | Practical consequence\n---|---\nTerminal first | The protocol must be usable by a person in a shell, without a browser or a special client.\nText before abstraction | Headers and responses are readable lines, not mandatory JSON or opaque binary.\nOne connection, one operation | Each TCP packet represents a clear intention: register, publish, list, preview, or recover.\nSymbolic civil identity | `CITIZEN` and `IDENTITY` are not merely username and password; they are how the city recognizes an inhabitant.\nDisk as the editorial source of truth | The protocol does not serve HTML; it changes Markdown in `content/` and triggers the static build.\nPragmatic compatibility | Legacy mode remains available for older administrative automation.\n\n* * *\n\n## 5. How the protocol works\n\nEvery SOLED/2 message begins with a fixed first line:\n\n\n SOLED/2\n\n\nThat line is the boundary between the second-generation parser and the legacy parser. If the first line is not `SOLED/2`, the server interprets the packet as legacy mode.\n\nAfter that, headers follow the format:\n\n\n KEY value\n\n\nA blank line ends the headers and, when applicable, begins the Markdown body. The packet ends with a line containing only a single dot:\n\n\n .\n\n\nThis convention makes the protocol easy to transmit manually from a terminal and easy to parse on the server.\n\nExample preview request:\n\n\n SOLED/2\n ACTION PREVIEW\n CITIZEN maria\n IDENTITY <citizen-secret>\n PATH citizens/maria/posts/note.md\n\n ---\n title: \"Note\"\n district: centro\n ---\n Markdown publication text.\n .\n\n\nThe operation above validates a draft without writing it to disk. Replacing `ACTION PREVIEW` with a normal publication action — or omitting `ACTION` when publication is treated as the default — changes the intention: the Markdown becomes a candidate for real writing into `content/`.\n\n* * *\n\n## 6. Main actions\n\nAction | Purpose | Controlled risk\n---|---|---\n`REGISTER` | Creates citizenship, handle, identity, and recovery code. The server stores only the identity hash. | Unauthorized entry through reserved or duplicate handles.\nPublish | Writes or updates an authorized Markdown file inside the citizen's tree. | Writing outside the citizen's territory or sending invalid front matter.\n`PREVIEW` | Runs full validation without altering the disk. It is the rehearsal before publication. | Accidentally publishing broken content.\n`LIST` | Lists Markdown files inside the citizen's permitted territory. | Leaking file structure outside the citizen's scope.\n`RECOVER` | Uses the recovery code to issue a new identity and invalidate the old one. | Permanent loss of access.\n`DELETE` | As a special body, removes an authorized Markdown file. | Unauthorized deletion.\n\n* * *\n\n## 7. Citizenship registration\n\nRegistration is the symbolic gate of the city. The client sends `ACTION REGISTER`, a `HANDLE`, and optionally a name. The server decides whether the handle is valid, whether it is reserved, and whether it has already been taken.\n\nOn success, the server responds with a secret identity and a recovery code.\n\n\n SOLED/2\n ACTION REGISTER\n HANDLE maria\n NAME Maria Silva\n\n .\n\n\nThe response includes `IDENTITY` and `RECOVERY`. The identity should be stored locally. The recovery code should be treated as an emergency document.\n\nThe server does not store the identity in clear text. It stores a hash and compares future proofs against that hash.\n\n* * *\n\n## 8. Publishing Markdown\n\nTo publish, the citizen provides `CITIZEN`, `IDENTITY`, and `PATH`.\n\nThe `PATH` is not arbitrary. It must remain inside the permitted space, usually something like:\n\n\n citizens/<handle>/\n\n\nValidation blocks directory escapes, forbidden paths, oversized payloads, nonexistent districts, and inconsistent front matter.\n\nWhen everything passes, the server writes the file into `content/` and runs the site build. The final visitor does not talk to SOLED/2. They see HTML served later over HTTPS from the regenerated public folder.\n\nExample publication:\n\n\n SOLED/2\n CITIZEN maria\n IDENTITY <citizen-secret>\n PATH citizens/maria/posts/first-post.md\n\n ---\n title: \"My first post\"\n district: centro\n ---\n This is a publication sent directly from the terminal.\n .\n\n\n* * *\n\n## 9. Preview: the brake that prevents disaster\n\n`PREVIEW` exists because direct publishing to production is too powerful to be blind.\n\nIt runs validation, reports warnings or errors, and writes nothing. In practice, it is the safe mode for scripts, assistants, and humans to check whether a packet is publishable before touching the disk.\n\nThis action also separates transport from editorial judgment. The protocol carries the file; the validation layer decides whether that file respects the city's model.\n\n* * *\n\n## 10. Identity recovery\n\n`RECOVER` solves an unavoidable failure in simple systems: people lose secrets.\n\nInstead of relying on email, passwords, OAuth, or an administrative dashboard, Soledade issues a recovery code during registration. Whoever holds that code can request a new identity. The previous identity stops working.\n\nThis keeps the design coherent: the city remains textual, the terminal remains sufficient, and the server does not need to store a reversible password.\n\n* * *\n\n## 11. Operational security\n\nSOLED/2 is not transport encryption. If port 1915 is exposed, the operation needs appropriate external protection: firewall rules, an SSH tunnel, IP policy, or an explicit decision to make the port public.\n\nThe protocol authenticates identity at the application level. It does not promise connection secrecy by itself.\n\nOperational safeguards include:\n\n * IP-based rate limiting to contain registration and publishing spam;\n * citizen status such as `active`, `suspended`, or `banned`;\n * blocking dangerous paths, including attempts to escape `content/`;\n * build locking to avoid regeneration races;\n * logs containing protocol, citizen, action, and IP for basic auditing;\n * preservation of legacy mode without mixing administrative privilege with ordinary citizenship.\n\n\n\n* * *\n\n## 12. Relationship with legacy mode\n\nThe first generation does not disappear. It remains available on the same port as legacy mode, identified by the absence of the `SOLED/2` first line.\n\nIts format is direct:\n\n\n <path>\n <administrative-token>\n <body>\n .\n\n\nThis preserves older scripts and allows mayor-level operations without forcing immediate migration.\n\nBut legacy mode is not citizenship. It is administration. That distinction matters: SOLED/2 was born for many authors with their own scope; legacy mode exists for centralized trusted automation.\n\n* * *\n\n## 13. What SOLED/2 is not\n\nSOLED/2 is not:\n\n * REST, JSON-RPC, or GraphQL;\n * a universal standard;\n * a replacement for Git;\n * an HTML-serving protocol;\n * the read protocol on port 1900;\n * a substitute for firewall policy, exposure decisions, or operational hygiene.\n\n\n\nIt is the official write protocol of Soledade.\n\n* * *\n\n## 14. Why this choice makes sense\n\nThe choice behind SOLED/2 is stubbornly simple, and that is its virtue.\n\nFor a project like Soledade, adopting a conventional web stack would solve publishing, but it would erase part of the system's identity. The protocol makes the form match the content: a textual city, governed by files, accessible through a terminal, and understandable by direct reading.\n\nThe gain is not only technical. It is cultural.\n\nPublishing stops being a click inside an invisible form and becomes the act of sending a formal letter to the city. The server reads it, validates it, records it, and rebuilds the public showcase. That minimal friction creates ritual without becoming bureaucracy.\n\nIn short: SOLED/2 exists because Soledade needed to grow from blog to city without losing its plain-text soul.\n\n* * *\n\n## 15. Quick specification\n\nItem | Value\n---|---\nTransport | TCP\nDefault port | `1915`\nEncoding | UTF-8\nFirst line | `SOLED/2`\nHeader format | `KEY value`\nSeparator | Blank line between headers and body\nPacket terminator | A line containing only `.`\nEditorial unit | Markdown file inside `content/`\nCanonical implementation | `soledade/post_server.py`\nCitizenship state | `data/citizens.json` with identity hashes\n\n* * *\n\n## 16. One-sentence pitch\n\nI created SOLED/2 so that Soledade could publish Markdown through port 1915 as a textual city: each person registers a handle, receives a secret identity, sends terminal-readable packets, and the server validates, writes, and rebuilds the site without WordPress, without a web dashboard, and without abandoning the simplicity of TCP.\n\n* * *\n\n## 17. Future evolution\n\nIf SOLED/3 ever exists, the correct path is to preserve predictability: a new first line, temporary compatibility with SOLED/2, updated documentation, new tests, and old responses preserved whenever possible.\n\nA protocol change without a clear contract is just a bug with marketing.\n\nUntil then, SOLED/2 can evolve through new `ACTION`s and optional headers, as long as it does not break existing clients or change the meaning of fundamental responses.\n\n* * *\n\n## Appendix A — Mental model of the flow\n\n\n ncat/script client\n |\n | TCP :1915, UTF-8, packet terminated by dot\n v\n soledade.post_server\n |\n |-- first line == SOLED/2 -> SOLED/2 parser\n | |-- REGISTER / RECOVER -> citizens and identity\n | |-- LIST -> permitted files\n | |-- PREVIEW -> validation without writing\n | `-- publication -> validation, writing, and build\n |\n `-- otherwise -> legacy parser with administrative token\n\n content/*.md -> build_site() -> public/*.html -> Caddy/HTTPS\n\n\n* * *\n\n## Appendix B — Common errors\n\nResponse | Meaning\n---|---\n`400 bad request` | Malformed message or missing required header.\n`401 invalid identity` | Handle and identity do not match.\n`401 invalid recovery` | Invalid recovery code.\n`403 forbidden` | Operation not allowed for that citizen or path.\n`403 handle reserved` | Handle reserved for the system or mayor.\n`409 handle taken` | Handle already registered.\n`413 payload too large` | Packet exceeds the allowed limit.\n`429 rate limited` | Too many operations in a short interval.\n\n* * *\n\n## Final note\n\nSOLED/2 is intentionally small. Its purpose is not to impress through complexity, but to create a clear, auditable contract that fits Soledade's technical aesthetic.",
"title": "SOLED/2 — The Soledade Publishing Protocol",
"updatedAt": "2026-06-03T10:02:44.328Z"
}