{"id":44437141,"url":"https://github.com/levelfourab/windshift-go","last_synced_at":"2026-02-12T14:01:05.959Z","repository":{"id":247071674,"uuid":"824459188","full_name":"LevelFourAB/windshift-go","owner":"LevelFourAB","description":"Go client for Windshift events","archived":false,"fork":false,"pushed_at":"2025-11-04T15:22:10.000Z","size":123,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-04T17:17:50.628Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LevelFourAB.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":"2024-07-05T07:20:07.000Z","updated_at":"2025-11-04T15:22:13.000Z","dependencies_parsed_at":"2024-07-31T09:06:53.736Z","dependency_job_id":null,"html_url":"https://github.com/LevelFourAB/windshift-go","commit_stats":null,"previous_names":["levelfourab/windshift-go"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/LevelFourAB/windshift-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LevelFourAB%2Fwindshift-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LevelFourAB%2Fwindshift-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LevelFourAB%2Fwindshift-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LevelFourAB%2Fwindshift-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LevelFourAB","download_url":"https://codeload.github.com/LevelFourAB/windshift-go/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LevelFourAB%2Fwindshift-go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29367813,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-12T08:51:36.827Z","status":"ssl_error","status_checked_at":"2026-02-12T08:51:26.849Z","response_time":55,"last_error":"SSL_read: 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":[],"created_at":"2026-02-12T14:00:53.060Z","updated_at":"2026-02-12T14:01:05.952Z","avatar_url":"https://github.com/LevelFourAB.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# windshift-go\n\nWindshift is an event-stream framework on top of [NATS Jetstream](https://nats.io/),\nthat enforces strong typing using [Protobuf](https://protobuf.dev/) with support\nfor tracing using [OpenTelemetry](https://opentelemetry.io).\n\n## Features\n\n- 🌊 Stream management, declare streams and bind subjects to them, with\n  configurable retention and limits\n- 📄 Event data in Protobuf format, for strong typing and schema evolution\n- 📤 Publish events to subjects, with idempotency and [OpenTelemetry tracing](https://opentelemetry.io)\n- 📥 Durable consumers with distributed processing\n- 🕒 Ephemeral consumers for one off event processing\n- 🔄 Automatic redelivery of failed events\n- 🔔 Ability to extend processing time by pinging events\n\n## Using\n\n```bash\ngo get github.com/levelfourab/windshift-go\n```\n\nCreating a client:\n\n```go\nnatsClient, err := nats.Connect(\"localhost:4222\")\neventsClient, err := windshift.NewEvents(natsClient)\n```\n\n## Protobuf\n\nData in Windshift is represented using [Protobuf](https://protobuf.dev/). You\nwill need to generate Go code from your Protobuf files to use the library.\n\n[Buf](https://buf.build/) can be used to generate the necessary files, and\nprovides linting and breaking change detection.\n\n## Streams\n\nStreams in Windshift store events. They can be used to replay events, or to\nstore events for a certain amount of time. Streams can be configured with\nretention policies, sources, and more.\n\nStreams can receive events from multiple subjects, but each subject can only\nbe bound to one stream. For example, if you have a stream called `orders` which\nreceives events from the `orders.created` subject, you cannot create another\nstream that also receives events from `orders.created`.\n\nSee [Streams in the NATS Jetstream documentation](https://docs.nats.io/nats-concepts/jetstream/streams)\nfor more details.\n\nExample:\n\n```go\nstream, err := eventsClient.EnsureStream(\n  ctx,\n  \"orders\", \n  streams.WithSubjects(\"orders.\u003e\"),\n)\n```\n\nIt is possible to control the retention policy of the stream via things like\n`streams.MaxAge`, `streams.MaxBytes`, and `streams.MaxEvents`.\n\nExample:\n\n```go\nstream, err := eventsClient.EnsureStream(\n  ctx,\n  \"orders\", \n  streams.WithSubjects(\"orders.\u003e\"),\n  streams.MaxAge(30 * time.Days),\n)\n```\n\n## Publishing events\n\nEvents can be published if there is a stream that matches the subject of the\nevent. The event will be stored in the stream and can be consumed by consumers\nsubscribed to the stream.\n\nExample:\n\n```go\neventsClient.Publish(ctx, \"orders.created\", \u0026ordersv1.OrderCreated{\n  ID: \"123\",\n})\n```\n\nFeatures:\n\n- Timestamps for when the event occurred can be specified with `timestamp`.\n- Idempotency keys can be specified using `idempotency_key`. If an event with\n  the same idempotency key has already been published, the event will not be\n  published again. The window for detecting duplicates can be configured via\n  the stream.\n- Optimistic concurrency control can be used via `expected_last_id`. If the\n  last event in the stream does not have the specified id, the event will not\n  be published.\n\n## Defining a consumer\n\nConsumers in Windshift are used to subscribe to events in streams. Consumers\ncan be ephemeral or durable. Ephemeral consumers are automatically removed\nafter they have been inactive for a certain amount of time.\n\nTo create a durable consumer give it a name:\n\n```go\nconsumer, err := events.EnsureConsumer(ctx, \"orders\", consumers.WithName(\"idOfConsumer\"))\n```\n\nTo create an ephemeral consumer omit the name:\n\n```go\nconsumer, err := events.EnsureConsumer(ctx, \"orders\")\n```\n\nConsumers can be configured with options. Options include:\n\n- `WithSubjects` - a subset of subjects to subscribe to.\n- `WithProcessingTimeout` - the time to wait for an event to be acknowledged,\n  rejected, or pinged before requeuing it.\n- `WithConsumeFrom` - the position in the stream to start consuming events from.\n\n## Subscribing to events\n\nEvents can be consumed by subscribing to a consumer. For durable consumers\nmultiple subscriptions may be made, and events will be distributed between\nthe subscriptions.\n\nExample:\n\n```go\nevents, err := eventsClient.Subscribe(ctx, \"orders\", \"idOfConsumer\")\n\nfor event := range events {\n  // Context includes tracing data from the publishing of the event\n  ctx := event.Context()\n\n  // Unmarshal the event data to process it\n  data, err := event.UnmarshalNew()\n  \n  // Acknowledge that event was processed (or reject it)\n  err := event.Ack(ctx)\n  if err != nil {\n    // Handle error\n  }\n}\n```\n\nSubscriptions stay active as long as the context remains uncanceled and will\nreconnect to NATS if the connection is lost.\n\n### Acknowledging and rejecting events\n\nEvents need to be acknowledge or rejected to indicate if we have successfully\nhandled them or not. If an event is not acknowledged or rejected within the\nprocessing timeout for the consumer it will be redelivered.\n\nTo acknowledge an event call `Ack`:\n\n```go\nerr := event.Ack(ctx)\n```\n\nTo reject an event call `Reject`:\n\n```go\n// Reject and requeue the event for immediate redelivery\nerr := event.Reject(ctx)\n\n// Reject and requeue the event for redelivery after a delay\nerr := event.Reject(ctx, events.WithRedeliveryDelay(5 * time.Minute))\n\n// Reject permanently\nerr := event.Reject(ctx, events.Permanently())\n\n// Reject with customizable delay\nerr := event.Reject(ctx, events.WithRedeliveryDecider(func(event events.Event) time.Duration {\n  return ... // Calculate delay based on delivery attempt, headers, data etc\n}))\n```\n\nTo extend the time available for processing an event, the event can be pinged.\n\n```go\nerr := event.Ping(ctx)\n```\n\nIt also possible to control how the library attempts to retry acking or rejecting\nan event by using either `events.WithNoRetry` or `events.WithBackoff`:\n\n```go\nerr := event.Ack(ctx, events.WithBackoff(\n  delays.StopAfterMaxTime(delays.Exponential(10*time.Millisecond, 2), 5*time.Second),\n))\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flevelfourab%2Fwindshift-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flevelfourab%2Fwindshift-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flevelfourab%2Fwindshift-go/lists"}