{"id":16965044,"url":"https://github.com/propensive/coaxial","last_synced_at":"2025-08-17T20:09:47.433Z","repository":{"id":212362659,"uuid":"731202982","full_name":"propensive/coaxial","owner":"propensive","description":"Socket handling for Scala","archived":false,"fork":false,"pushed_at":"2025-02-11T20:35:53.000Z","size":1429,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-11T17:56:10.134Z","etag":null,"topics":["communication","network-protocols","scala","socket-client","socket-server","sockets","tcp","tcp-ip-sockets","udp"],"latest_commit_sha":null,"homepage":"","language":"Scala","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/propensive.png","metadata":{"files":{"readme":".github/readme.md","changelog":null,"contributing":".github/contributing.md","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":"2023-12-13T15:05:48.000Z","updated_at":"2025-02-11T20:35:56.000Z","dependencies_parsed_at":"2023-12-21T21:38:48.587Z","dependency_job_id":"4133d6d8-1744-4a50-af6f-26b57907f1ec","html_url":"https://github.com/propensive/coaxial","commit_stats":null,"previous_names":["propensive/coaxial"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/propensive/coaxial","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fcoaxial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fcoaxial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fcoaxial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fcoaxial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/propensive","download_url":"https://codeload.github.com/propensive/coaxial/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/propensive%2Fcoaxial/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270899579,"owners_count":24664720,"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-17T02:00:09.016Z","response_time":129,"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":["communication","network-protocols","scala","socket-client","socket-server","sockets","tcp","tcp-ip-sockets","udp"],"created_at":"2024-10-13T23:44:54.003Z","updated_at":"2025-08-17T20:09:47.370Z","avatar_url":"https://github.com/propensive.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"[\u003cimg alt=\"GitHub Workflow\" src=\"https://img.shields.io/github/actions/workflow/status/propensive/coaxial/main.yml?style=for-the-badge\" height=\"24\"\u003e](https://github.com/propensive/coaxial/actions)\n[\u003cimg src=\"https://img.shields.io/discord/633198088311537684?color=8899f7\u0026label=DISCORD\u0026style=for-the-badge\" height=\"24\"\u003e](https://discord.com/invite/MBUrkTgMnA)\n\u003cimg src=\"/doc/images/github.png\" valign=\"middle\"\u003e\n\n# Coaxial\n\n__Socket handling for Scala__\n\n_Coaxial_ provides a generic framework for client/server communications over a\nnetwork, abstracting over protocol differences, including statefulness.\nImplementations are provided for sending and receiving UDP packets, and\nconnecting and serving TCP connections.\n\n## Features\n\n- provides an abstract framework for data transmission through sockets\n- supports both stateful (e.g. TCP) and stateless (e.g. UDP) connections\n- server and client implementations for UDP, TCP and UNIX domain sockets\n- allows servers and clients to process connections as state machines\n- uses a safe, functional API\n\n\n## Availability\n\n\n\n\n\n\n\n## Getting Started\n\nAll terms and types are defined in the `coaxial` package, which can be imported\nwith:\n```scala\nimport coaxial.*\n```\n\n### Transmitting data\n\nIn general, a client connects to a listing server by calling either `transmit`\n(for stateless transmission) or `connect` (to start a stateful connection) on\nan endpoint. The endpoint type determines the transmission protocol.\n\nEndpoint types include,\n - local UDP ports, e.g. `udp\"3480\"`\n - local TCP ports, e.g. `tcp\"2205\"`\n - remote ports, e.g. `host\"example.com\" on tcp\"8844\"` or\n   `ip\"192.168.1.24\" on udp\"1280\"`\n - UNIX domain sockets\n - any type for which an `Addressable` or `Connectable` typeclass instance\n   exists\n\nFor example, a simple message could be sent to a local UDP port with,\n```scala\nudp\"7327\".transmit(t\"Hello world!\")\n```\nThis will send a single UDP packet containing the bytes of the string `Hello\nworld!`, and nothing further. This is useful for \"fire-and-forget\" messaging.\n\nThe payload of the UDP packet is a `Text` string, but can be any type for which\na `Transmissible` typeclass instance exists, which includes `Text`, `Bytes` and\nanything that has a [Spectacular](https://github.com/propensive/spectacular/)\n`Encoder`.\n\nAll `Addressable` endpoints can have data transmitted to them, but some, such\nas TCP ports, can initiate a bidirectional connection. These are those\nendpoints for which a `Connectable` typeclass instance exists. `Connectable` is\na subtype of `Addressable`.\n\nStarting a stateful connection, with the `connect` method, requires a bit more\nwork. Its state is user-defined, and can be represented by any type. So a\nconnection requires the following:\n - the initial state\n - the type of the state, if it can't be inferred from the initial state\n - the initial message, in a type wich is `Transmissible`\n - a lambda to interact with the server, interpreting responses, modifying\n   state, and sending further messages\n\nOf these, the lambda is where most of the work is done. It will be executed\nsequentially for every chunk of data, as `Bytes`, received from the server, and\nmust choose how to process that data.\n\nThe return type of the lambda must be one of the following `Control` values:\n - `Reply(message)`, to respond to the server with a new message on the current\n   connection\n - `Continue(state)`, to update the state to a new value, and wait for more data\n   from the server\n - `Terminate`, to abort the connection\n - `Conclude(message, state)`, to respond to the server with a final message,\n   and update the state to a new value\n\nWithin the body of the lambda, the named contextual value, `state`, provides\nthe current state.\n\nThe return type of `connect` will be the final state value.\n\nHere is an trivial example:\n```scala\n(host\"remote.com\" on tcp\"1920\").connect(t\"\")(t\"DATA\\n\"): bytes =\u003e\n  val text = bytes.as[Text]\n  val state2 = state+text\n  if state2.length \u003c 100 then Control.Continue(state2)\n  else Control.Conclude(t\"END\\n\", state2)\n```\n\nThis will connect to TCP port 1920 on host `remote.com`, sending the initial\nrequest message, `DATA`. When the server (hopefully) responds with some textual\ndata, we convert the bytes to text and append them to the existing state,\naccessed with the named contextual value, `state`. If we have less than 100\ncharacters of text, then we continue waiting, updating the state to the new\nvalue. But if we have already received more than 100 characters, we conclude\nthe connection by sending a final message, `END`, to the server, updating the\nstate to its final value. The return value will be at least 100 characters of\ntext.\n\nIt should be clear from this example that more complex connection handlers are\npossible, and can be written in a style that's similar to folding over a\nstream.\n\nThe functional interface of `connect`, whose lambda _requires_ a `Control`\nvalue to be specified means that it's impossible to forget how to handle the\ninput; the programmer must decide to reply, continue, conclude or terminate the\nconnection, otherwise the code will not compile.\n\n### Implementing a Server\n\nWriting a server can be simpler than sending a client request. The `listen`\nmethod is available on any type with a `Bindable` typeclass instance, but this\nnotably includes:\n - UNIX domain sockets\n - TCP ports\n - UDP ports\n\nWe can write a server by calling the `listen` method on a `Bindable` type, and\nspecifying, by means of a lambda, how a request should be handled.\n\nCalling `listen` will start a new server in a separate thread. Any incoming\nrequest will dispatch it to the handler lambda. The type of the input will\ndepend on the type of the local endpoint. So a stateful connection will provide\nan input type which can support an interactive session, whereas a stateless\nconnection will provide just the payload.\n\nThe return value of the lambda will also be determined from the endpoint type,\nas appropriate for stateful and stateless connections.\n\nAs an example, a simple UDP server takes input as a `UdpPacket` and returns a\n`UdpResponse`. `UdpPacket` is a case class with fields: `data`, the payload\nbytes; `sender`, an `Ipv4` or `Ipv6` source address; and `port`, the remote UDP\nport.\n\n`UdpResponse` is an enumeration, which is either `Ignore` (if no response is\nexpected to the UDP packet), or `Reply(payload)` with the bytes to respond\nwith.\n\nSo a UDP server which responds to `PING` messages with `PONG`, but ignores\nother messages could be implemented with just:\n```scala\nval server = udp\"1722\".listen: packet =\u003e\n  if packet.as[Text] == t\"PING\" then Reply(t\"PONG\") else Ignore\n```\n\nThe return value, `server`, is a `SocketService` representing the server\nrunning in the background, and will be returned as soon as the port has been\nbound. It may be stopped at any time by calling its `stop` method, like so:\n```scala\nserver.stop()\n```\n\n\n\n\n\n## Status\n\nCoaxial is classified as __embryotic__. For reference, Soundness projects are\ncategorized into one of the following five stability levels:\n\n- _embryonic_: for experimental or demonstrative purposes only, without any guarantees of longevity\n- _fledgling_: of proven utility, seeking contributions, but liable to significant redesigns\n- _maturescent_: major design decisions broady settled, seeking probatory adoption and refinement\n- _dependable_: production-ready, subject to controlled ongoing maintenance and enhancement; tagged as version `1.0.0` or later\n- _adamantine_: proven, reliable and production-ready, with no further breaking changes ever anticipated\n\nProjects at any stability level, even _embryonic_ projects, can still be used,\nas long as caution is taken to avoid a mismatch between the project's stability\nlevel and the required stability and maintainability of your own project.\n\nCoaxial is designed to be _small_. Its entire source code currently consists\nof 382 lines of code.\n\n## Building\n\nCoaxial will ultimately be built by Fury, when it is published. In the\nmeantime, two possibilities are offered, however they are acknowledged to be\nfragile, inadequately tested, and unsuitable for anything more than\nexperimentation. They are provided only for the necessity of providing _some_\nanswer to the question, \"how can I try Coaxial?\".\n\n1. *Copy the sources into your own project*\n   \n   Read the `fury` file in the repository root to understand Coaxial's build\n   structure, dependencies and source location; the file format should be short\n   and quite intuitive. Copy the sources into a source directory in your own\n   project, then repeat (recursively) for each of the dependencies.\n\n   The sources are compiled against the latest nightly release of Scala 3.\n   There should be no problem to compile the project together with all of its\n   dependencies in a single compilation.\n\n2. *Build with [Wrath](https://github.com/propensive/wrath/)*\n\n   Wrath is a bootstrapping script for building Coaxial and other projects in\n   the absence of a fully-featured build tool. It is designed to read the `fury`\n   file in the project directory, and produce a collection of JAR files which can\n   be added to a classpath, by compiling the project and all of its dependencies,\n   including the Scala compiler itself.\n   \n   Download the latest version of\n   [`wrath`](https://github.com/propensive/wrath/releases/latest), make it\n   executable, and add it to your path, for example by copying it to\n   `/usr/local/bin/`.\n\n   Clone this repository inside an empty directory, so that the build can\n   safely make clones of repositories it depends on as _peers_ of `coaxial`.\n   Run `wrath -F` in the repository root. This will download and compile the\n   latest version of Scala, as well as all of Coaxial's dependencies.\n\n   If the build was successful, the compiled JAR files can be found in the\n   `.wrath/dist` directory.\n\n## Contributing\n\nContributors to Coaxial are welcome and encouraged. New contributors may like\nto look for issues marked\n[beginner](https://github.com/propensive/coaxial/labels/beginner).\n\nWe suggest that all contributors read the [Contributing\nGuide](/contributing.md) to make the process of contributing to Coaxial\neasier.\n\nPlease __do not__ contact project maintainers privately with questions unless\nthere is a good reason to keep them private. While it can be tempting to\nrepsond to such questions, private answers cannot be shared with a wider\naudience, and it can result in duplication of effort.\n\n## Author\n\nCoaxial was designed and developed by Jon Pretty, and commercial support and\ntraining on all aspects of Scala 3 is available from [Propensive\nO\u0026Uuml;](https://propensive.com/).\n\n\n\n## Name\n\n_Coaxial_ cable is designed for the transmission of high-frequency data with\nminimal loss, alluding to the function of transmission of data that this\nlibrary provides.\n\nIn general, Soundness project names are always chosen with some rationale,\nhowever it is usually frivolous. Each name is chosen for more for its\n_uniqueness_ and _intrigue_ than its concision or catchiness, and there is no\nbias towards names with positive or \"nice\" meanings—since many of the libraries\nperform some quite unpleasant tasks.\n\nNames should be English words, though many are obscure or archaic, and it\nshould be noted how willingly English adopts foreign words. Names are generally\nof Greek or Latin origin, and have often arrived in English via a romance\nlanguage.\n\n## Logo\n\nThe logo shows a cross-section of a coaxial connector.\n\n## License\n\nCoaxial is copyright \u0026copy; 2025 Jon Pretty \u0026 Propensive O\u0026Uuml;, and\nis made available under the [Apache 2.0 License](/license.md).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpropensive%2Fcoaxial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpropensive%2Fcoaxial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpropensive%2Fcoaxial/lists"}