{"id":27000564,"url":"https://github.com/petekubiak/post-haste","last_synced_at":"2026-01-19T04:01:00.063Z","repository":{"id":285617723,"uuid":"958735737","full_name":"petekubiak/post-haste","owner":"petekubiak","description":"A no-std async Rust library for creating modular projects","archived":false,"fork":false,"pushed_at":"2025-11-26T11:43:46.000Z","size":154,"stargazers_count":7,"open_issues_count":8,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-29T10:06:40.122Z","etag":null,"topics":["async","bare-metal","messaging","modular-architecture","no-std","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/petekubiak.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2025-04-01T17:17:04.000Z","updated_at":"2025-11-26T11:43:46.000Z","dependencies_parsed_at":"2025-04-01T19:26:19.821Z","dependency_job_id":"3f90441b-5fcc-45d1-a17b-92f42557379b","html_url":"https://github.com/petekubiak/post-haste","commit_stats":null,"previous_names":["petekubiak/post-haste"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/petekubiak/post-haste","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/petekubiak%2Fpost-haste","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/petekubiak%2Fpost-haste/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/petekubiak%2Fpost-haste/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/petekubiak%2Fpost-haste/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/petekubiak","download_url":"https://codeload.github.com/petekubiak/post-haste/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/petekubiak%2Fpost-haste/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28561599,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-19T03:31:16.861Z","status":"ssl_error","status_checked_at":"2026-01-19T03:31:15.069Z","response_time":67,"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":["async","bare-metal","messaging","modular-architecture","no-std","rust"],"created_at":"2025-04-04T03:27:22.940Z","updated_at":"2026-01-19T04:01:00.047Z","avatar_url":"https://github.com/petekubiak.png","language":"Rust","funding_links":[],"categories":["no-std crates"],"sub_categories":["WIP"],"readme":"# Post-haste\nA no_std, alloc-free async Rust library for creating modular projects.\n\n## Summary\nThe goal of this library is to provide a framework for a highly modularised code base.\nThere are two core components around which this framework is based: the Agent and the Postmaster.\n\n## Agents\nFunctionality of the application is divided up between a number of modules, dubbed \"Agents\".\nEach Agent is expected to have a single responsibility.\nPost-haste provides the Agent trait, which defines the interface for Agents and gives them the ability to be integrated into the rest of the system.\nThe defining features of an Agent are an instantiation function: `create()`, a task loop: `run()`, and an inbox.\n\nThe `create()` function is called automatically when the Agent is \"registered\" (see [Registering Agents](#registering-agents) below).\nThe body of this function should instantiate the Agent and populate it with any configuration it requires.\nThe `Config` associated type is used to assist in this.\n\nThe `run()` function is the Agent's main loop.\nThis is spawned as a standalone task in the executor, and is not expected to return (i.e. the lifetime of Agents is expected to be the same as the lifetime of the application).\nWhen an Agent is registered, it is assigned a mailbox.\nThe receiving end of the mailbox (the inbox) is passed in as an argument to the `run()` function.\nIn the vast majority of cases, the core logic of the Agent's loop will be to await messages arriving in its inbox and perform actions based on what is received.\n\n## The Postmaster\nThe postmaster provides the mechanism by which Agents are able to communicate, and by which data moves around the system.\n\n### Initialisation\nIn order to allow the Postmaster to be `no_std` and `alloc`-free, its logic requires knowledge about the project to function.\nSpecifically, it needs to know the number of Agents which will be running and the payload structures which the messages will contain.\nTo achieve this, the Postmaster logic must be written at compile-time by the `init_postmaster!()` macro.\nThe two arguments to the macro are of course the `Address` type and the `Payload` type, both defined by your project.\nThe `init_postmaster!()` macro takes an optional third argument, the default timeout that the Postmaster should use when sending messages in microseconds.\nIf this optional argument is left out, the Postmaster will use a timeout of 1 ms (1000 us).\nFor more information on message sending timeout, see [Communicating with Agents](#communicating-with-agents) below.\nThe output of the macro is a `postmater` module, containing the Postmaster's public interface.\n\n### Registering Agents\nOnce you have defined an Agent type as described above, it is instantiated using the `postmaster::register_agent!()` macro.\nThis macro takes the following arguments:\n- A handle to the executor's Spawner (only in Embassy)\n- The Address to which the instance will be registered\n- The type of Agent being instantiated\n- Config for the Agent in the form of an instance of its associated `Config` type\n- (Optional) The size of the Agent's message queue\n\nWithin this macro, the Agent's message queue is created, the Agent instance is created and a task is spawned for its main loop.\nThe Agent can be considered active and ready to receive messages immediately following its registration.\n\n### Communicating with Agents\nThe standard way to communicate with an Agent is by sending it messages using the Postmaster.\nThe `postmaster` module generated by `init_postmaster!()` provides a set of functions for this purpose.\n`postmaster::message()` takes source and destination addresses, plus a message payload, and returns a `MessageBuilder`.\nThe `MessageBuilder` allows further configuration of how the message is sent (explained further below).\nOnce configured, the message is sent by calling the `MessageBuilder`'s `send()` function.\n\nWhen sending a message, it may be configured with a \"timeout\" and a \"delay\".\nUpon attempting to send a message it may not be possible to immediately push the message onto the recipient's queue, for example if said queue is already full.\nThis is the purpose of the timeout: the `send()` function returns a future which will resolve either when the message has been successfully posted, or when the timeout expires.\nBy default, the timeout is 1 ms.\nSending a message with a \"delay\" means that the `send()` function will immediately return, but the message will only be added to the recipient's queue after the delay is complete.\n\nThe `postmaster` module also contains a couple of shortcut functions for sending messages:\n- `postmaster::send()` which will attempt to send the message immediately with the default timeout of 1 ms.\n- `postmaster::try_send()` which will attempt to send the message immediately, but will not wait: it will return immediately.\n\nIn all cases, what the recipient receives when it accesses its inbox is a `postmaster::Message` struct, which contains the source address and the message payload.\n\nPlease note: the `Message` and `Address` associated types in the `Agent` trait correspond to the auto-generated `Message` type and the user-provided `Address` list respectively.\n\n### Other features\nA high level overview of the Postmaster's diagnostics can be obtained using the `postmaster::get_diagnostics()` function.\nCurrently this just contains a tally of the number of messages successfully sent, and the number of send failures since boot.\n\nIt is also possible to register a standalone mailbox on the system, without associating it with an Agent, using `postmaster::register()`.\nThis might for example be used to communicate back to the main task of the project, or to provide a \"debug\" address for debug messages to be sent.\n\nThe default timeout used by the Postmaster when a message is sent with no specific timeout configuration can be changed using `postmaster::set_timeout()`, taking a value in microseconds.\n\n### Advanced configuration\n#### Delayed message pool (Embassy only)\nWhen using post-haste on bare metal targets with Embassy, delayed messages are held in a finite pool while they await the expiry of their delay duration.\nBy default, the size of this pool is 8.\nIf at any point the pool is full, any attempt to send a delayed message will result in a `DelayedMessagePoolFull` error, and the message will not be sent.\nThe size of the pool can be modified by setting the `DELAYED_MESSAGE_POOL_SIZE` environment variable.\nPlease note however that increasing the pool size will increase static memory usage.\n\n## Example usage\nThe following forms the core of the code layout for a baremetal project built upon post_haste (excluding any architecture-specific code and dependencies):\n```rust\n#![no_std]\n\n// NOTE: This feature is currently required in order to generate the correct number of mailboxes based on the number of provided addresses (avoiding alloc)\n// Therefore, the project must be compiled with the nightly compiler\n#![feature(variant_count)]\n\nuse embassy_executor::Spawner;\n\nuse post_haste::agent::Agent;\nuse post_haste::init_postmaster;\n\n/// The list of Agent addresses, used to identify the source and destination for messages.\n/// Each Agent must have a unique address\nenum Address {\n  AgentA,\n  AgentB,\n  // ...\n}\n\n/// Top-level definition of messages used in the system.\nenum Payloads {\n  General(GeneralPayloads),\n  // ...\n}\n\n/// A sub-category of messages (this heirachical ordering isn't necessary, but is highly recommended for organisation)\nenum GeneralPayloads {\n  Hello,\n  // ...\n}\n\n// Generates the postmaster logic and initialises the postmaster for use within the project\ninit_postmaster!(Address, Payloads);\n\nstruct MyAgent {\n  address: Address\n}\n\nimpl Agent for MyAgent {\n  type Address = Address;\n  type Message = postmaster::Message;\n  type Config = ();\n\n  async fn create(address: Self::Address, _: Self::Config) -\u003e Self {\n    // Initialisation code goes here...\n    Self { address }\n  }\n\n  async fn run(self, inbox: post_haste::agent::Inbox\u003cSelf::Message\u003e) -\u003e ! {\n    loop {\n      let received_message = inbox.recv().await.unwrap();\n      match received_message {\n        Payloads::Hello =\u003e postmaster::send(received_message.source, self.address, Payloads::Hello).await.unwrap();\n        // ...\n      }\n    }\n  }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n  const QUEUE_SIZE: usize = 8;\n  postmaster::register_agent!(spawner, AgentA, MyAgent, ());\n  postmaster::register_agent!(spawner, AgentB, MyAgent, (), QUEUE_SIZE);\n\n  loop {\n    // ...\n  }\n}\n```\n\nWhile the framework was originally developed for no_std baremetal environments, it is also fully compatible with tokio.\n- [tokio_basic.rs](examples/tokio_basic.rs) gives a very simple example of two Agents exchanging messages.\n- [showcase.rs](examples/showcase.rs) follows the same concept, but aims to demonstrate some useful patterns within the framework.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpetekubiak%2Fpost-haste","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpetekubiak%2Fpost-haste","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpetekubiak%2Fpost-haste/lists"}