{"id":49112983,"url":"https://github.com/false-systems/polku","last_synced_at":"2026-04-21T05:37:48.492Z","repository":{"id":331105429,"uuid":"1124630191","full_name":"false-systems/polku","owner":"false-systems","description":" A programmable Rust message pipeline that transforms, buffers, and routes messages between internal services.","archived":false,"fork":false,"pushed_at":"2026-04-12T20:58:54.000Z","size":698,"stargazers_count":1,"open_issues_count":46,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-12T22:25:24.893Z","etag":null,"topics":["grpc","json","messaging","microservices","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/false-systems.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","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-12-29T10:44:33.000Z","updated_at":"2026-04-12T20:58:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/false-systems/polku","commit_stats":null,"previous_names":["yairfalse/polku","false-systems/polku"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/false-systems/polku","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/false-systems%2Fpolku","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/false-systems%2Fpolku/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/false-systems%2Fpolku/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/false-systems%2Fpolku/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/false-systems","download_url":"https://codeload.github.com/false-systems/polku/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/false-systems%2Fpolku/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32079470,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-21T02:38:07.213Z","status":"ssl_error","status_checked_at":"2026-04-21T02:38:06.559Z","response_time":128,"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":["grpc","json","messaging","microservices","rust"],"created_at":"2026-04-21T05:37:47.441Z","updated_at":"2026-04-21T05:37:48.485Z","avatar_url":"https://github.com/false-systems.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# POLKU\n\n**The path your events take.**\n\n[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)\n[![Rust](https://img.shields.io/badge/rust-1.85%2B-orange.svg)](https://www.rust-lang.org)\n\n```\nMessages in  ───\u003e  POLKU  ───\u003e  Messages out\n                (transform)\n                (filter)\n                (route)\n```\n\n---\n\n## What is POLKU?\n\nPOLKU is a **programmable protocol hub**. Messages come in, get transformed, and go out to multiple destinations. Routing logic is Rust code, not config files.\n\n```\n                     ┌────────────────────────────────────────────────────┐\n                     │                      POLKU                         │\n                     │                                                    │\n   Your App ────────\u003e│  Ingestors ──\u003e Middleware ──\u003e Buffer ──\u003e Emitters  │────────\u003e gRPC\n                     │      │              │                       │      │\n   Agents ──────────\u003e│      │         [transform]                  │      │────────\u003e Webhook\n                     │      │         [filter]                     │      │\n   Webhooks ────────\u003e│      │         [route]                      │      │────────\u003e Stdout\n                     │      │         [validate]                   │      │\n                     │      v         [aggregate]                  v      │\n                     │  ┌────────┐                           ┌────────┐  │\n                     │  │ Plugin │  \u003c── gRPC ──\u003e             │ Plugin │  │\n                     │  │ (any   │                           │ (any   │  │\n                     │  │  lang) │                           │  lang) │  │\n                     │  └────────┘                           └────────┘  │\n                     └────────────────────────────────────────────────────┘\n```\n\n**Why POLKU?**\n\n- **No YAML** - Routing logic is code, not config files\n- **Any language** - Write plugins in Python, Go, Rust, whatever (gRPC interface)\n- **Tiny** - 10-20MB memory, no external dependencies\n- **Fast** - 178k+ events/sec streaming, ~1.3k/sec unary\n- **Observable** - 40+ Prometheus metrics, pipeline pressure gauge, health endpoints\n\n---\n\n## Installation\n\n### From Source\n\n```bash\ngit clone https://github.com/false-systems/polku\ncd polku\n\n# Build the gateway binary\ncargo build --release\n\n# Binary at ./target/release/polku-gateway\n```\n\n### Docker\n\n```bash\ndocker build -t polku-gateway .\ndocker run -p 50051:50051 -p 9090:9090 polku-gateway\n```\n\n### As a Library\n\nAdd to your `Cargo.toml`:\n\n```toml\n[dependencies]\npolku-runtime = { git = \"https://github.com/false-systems/polku\" }\n```\n\n---\n\n## Usage\n\n### Standalone Gateway\n\nRun the binary — it listens for gRPC on port 50051 and serves Prometheus metrics on port 9090:\n\n```bash\n# Debug mode: prints messages to stdout\n./polku-gateway\n\n# Router mode: forward to downstream gRPC endpoints\nPOLKU_EMIT_GRPC_ENDPOINTS=host1:50051,host2:50051 ./polku-gateway\n\n# With JSON structured logging\nPOLKU_LOG_FORMAT=json POLKU_LOG_LEVEL=debug ./polku-gateway\n```\n\nSend a message:\n\n```bash\ngrpcurl -plaintext -d '{\n  \"source\": \"my-app\",\n  \"cluster\": \"dev\",\n  \"payload\": {\n    \"events\": {\n      \"events\": [{\n        \"id\": \"1\",\n        \"timestamp_unix_ns\": 0,\n        \"source\": \"my-app\",\n        \"event_type\": \"user.signup\",\n        \"payload\": \"aGVsbG8=\"\n      }]\n    }\n  }\n}' localhost:50051 polku.v1.Gateway/SendEvent\n```\n\n### As a Rust Library\n\nDefine your pipeline in your own crate — POLKU handles tracing, metrics, gRPC, and shutdown:\n\n```rust\nuse polku_runtime::prelude::*;\n\n#[tokio::main]\nasync fn main() -\u003e anyhow::Result\u003c()\u003e {\n    polku_runtime::run(|hub| async move {\n        Ok(hub\n            .ingestor(JsonIngestor::new())\n            .middleware(Filter::new(|msg: \u0026Message| {\n                msg.message_type.starts_with(\"user.\")\n            }))\n            .middleware(Validator::json())\n            .emitter(StdoutEmitter::pretty()))\n    }).await\n}\n```\n\nYour closure receives a `Hub` pre-configured from environment variables (buffer capacity, batch size, flush interval). You add your ingestors, middleware, and emitters — the runtime does the rest.\n\nFor more control over ports or to disable gRPC:\n\n```rust\nuse polku_runtime::prelude::*;\n\n#[tokio::main]\nasync fn main() -\u003e anyhow::Result\u003c()\u003e {\n    RuntimeBuilder::new()\n        .grpc_addr(\"0.0.0.0:50052\".parse()?)\n        .metrics_port(9091)\n        .configure(|hub| async move {\n            let grpc = GrpcEmitter::with_endpoints(vec![\n                \"downstream:50051\".into(),\n            ]).await?;\n\n            Ok(hub\n                .ingestor(JsonIngestor::new())\n                .emitter(grpc))\n        }).await\n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eAdvanced: direct Hub wiring without the runtime\u003c/summary\u003e\n\nIf you need full control and want to manage tracing, metrics, and the gRPC server yourself:\n\n```rust\nuse polku_gateway::*;\nuse std::sync::Arc;\n\n#[tokio::main]\nasync fn main() -\u003e anyhow::Result\u003c()\u003e {\n    let (_, hub_sender, runner) = Hub::new()\n        .buffer_capacity(50_000)\n        .batch_size(500)\n        .flush_interval_ms(50)\n        .middleware(Filter::new(|msg: \u0026Message| msg.message_type.starts_with(\"user.\")))\n        .middleware(Validator::json())\n        .emitter(StdoutEmitter::pretty())\n        .build();\n\n    tokio::spawn(runner.run());\n\n    let registry = Arc::new(PluginRegistry::new());\n    let service = polku_gateway::server::GatewayService::with_hub(\n        registry, hub_sender,\n    );\n\n    tonic::transport::Server::builder()\n        .add_service(service.into_server())\n        .serve(\"[::1]:50051\".parse()?)\n        .await?;\n\n    Ok(())\n}\n```\n\n\u003c/details\u003e\n\n### Kubernetes\n\nPOLKU runs as a single pod. Minimal deployment:\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: polku\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: polku\n  template:\n    metadata:\n      labels:\n        app: polku\n    spec:\n      containers:\n        - name: polku\n          image: polku-gateway:latest\n          ports:\n            - containerPort: 50051  # gRPC\n            - containerPort: 9090   # Metrics\n          env:\n            - name: POLKU_GRPC_ADDR\n              value: \"0.0.0.0:50051\"\n            - name: POLKU_METRICS_ADDR\n              value: \"0.0.0.0:9090\"\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: polku\nspec:\n  selector:\n    app: polku\n  ports:\n    - name: grpc\n      port: 50051\n    - name: metrics\n      port: 9090\n```\n\n---\n\n## The Pipeline\n\n```\n┌─────────────────────────────────────────────────────────────────────┐\n│                          MESSAGE FLOW                                │\n│                                                                      │\n│   INGEST            MIDDLEWARE            BUFFER            EMIT     │\n│  ┌───────┐        ┌───────────┐        ┌───────────┐     ┌───────┐ │\n│  │ bytes │───────\u003e│  Filter   │───────\u003e│   Ring    │────\u003e│ Fan   │ │\n│  │   |   │        │  Enrich   │        │  Buffer   │     │ Out   │ │\n│  │Message│        │  Route    │        │  (async)  │     │       │ │\n│  └───────┘        │  Validate │        │  Tiered   │     └───────┘ │\n│                   │  Aggregate│        └───────────┘               │\n│                   └───────────┘                                     │\n└─────────────────────────────────────────────────────────────────────┘\n```\n\n1. **Ingestors** - Parse bytes into Messages (protobuf, JSON, NDJSON, custom via gRPC plugin)\n2. **Middleware** - Transform, filter, enrich, validate, route, aggregate, sample, rate-limit, deduplicate\n3. **Buffer** - Decouple ingestion from emission (backpressure). Standard ring buffer or tiered with zstd compression.\n4. **Emitters** - Send to destinations (gRPC, webhooks, stdout, custom via gRPC plugin). Optional resilience wrappers: retry, circuit breaker, failure capture.\n\nThe core type flowing through the pipeline is `Message` — protocol-agnostic, zero-copy:\n\n```rust\npub struct Message {\n    pub id: MessageId,                        // 16-byte ULID (Copy)\n    pub timestamp: i64,                       // Unix nanos\n    pub source: InternedStr,                  // Interned, O(1) clone\n    pub message_type: InternedStr,            // Interned, O(1) clone\n    pub metadata: Option\u003cBox\u003cHashMap\u003c...\u003e\u003e\u003e,  // Lazy-allocated\n    pub payload: Bytes,                       // Zero-copy (Arc-based)\n    pub route_to: SmallVec\u003c[String; 2]\u003e,      // Inline for 0-2 targets\n}\n```\n\nThe proto `Event` type only exists at gRPC boundaries (wire format). Inside the pipeline, everything is `Message`.\n\n---\n\n## Built-in Components\n\n### Ingestors\n| Name | Sources | Description |\n|------|---------|-------------|\n| `PassthroughIngestor` | `passthrough`, `polku` | Decodes protobuf `Event` into `Message` |\n| `JsonIngestor` | `json`, `json-lines`, `ndjson` | JSON objects, arrays, or newline-delimited |\n| `ExternalIngestor` | configurable | Delegates to any gRPC plugin |\n\n### Middleware\n| Name | What it does |\n|------|--------------|\n| `Filter` | Drop messages that don't match a predicate |\n| `Transform` | Apply a function to each message |\n| `Router` | Content-based routing (sets `route_to` targets) |\n| `RateLimiter` | Global token-bucket rate limiting |\n| `Throttle` | Per-source rate limiting with LRU eviction |\n| `Sampler` | Probabilistic sampling (lock-free xorshift64) |\n| `Deduplicator` | Drop duplicate IDs within a TTL window |\n| `Enricher` | Add metadata from async lookups (DB, API) |\n| `Validator` | Validate messages (built-ins: JSON, non-empty, max-size) |\n| `Aggregator` | Batch N messages into 1 (strategies: JSON array, concat, first, last) |\n\n### Emitters\n| Name | Description |\n|------|-------------|\n| `StdoutEmitter` | Print to console (compact or pretty format) |\n| `GrpcEmitter` | Send to downstream gRPC endpoints with load balancing |\n| `WebhookEmitter` | HTTP POST JSON to any URL |\n| `ExternalEmitter` | Delegate to any gRPC plugin |\n\n### Resilience Wrappers\nComposable layers that wrap any emitter:\n\n| Wrapper | What it does |\n|---------|--------------|\n| `RetryEmitter` | Exponential backoff with jitter |\n| `CircuitBreakerEmitter` | Closed/Open/Half-Open state machine |\n| `FailureCaptureEmitter` | Buffer failed messages for debugging |\n\nCompose with the builder:\n```rust\nlet emitter = ResilientEmitter::wrap(my_emitter)\n    .with_default_retry()\n    .with_default_circuit_breaker()\n    .with_failure_capture(buffer)\n    .build();\n```\n\n---\n\n## FALSE Protocol\n\nPOLKU has first-class support for the [FALSE Protocol](https://github.com/false-systems/false-protocol) (Functional AI-native Semantic Events). The `polku-fp` crate provides plugins for ingesting, routing, correlating, and aggregating Occurrences.\n\n```toml\n[dependencies]\npolku-fp = { git = \"https://github.com/false-systems/polku\" }\n```\n\n### Occurrence Pipeline\n\n```rust\nuse polku_fp::*;\n\npolku_runtime::run(|hub| async move {\n    Ok(hub\n        .ingestor(OccurrenceIngestor::new())\n        .middleware(OccurrenceCorrelator::new().ttl_secs(300))\n        .middleware(OccurrenceAggregator::new(5).ttl_secs(600))\n        .middleware(OccurrenceRouter::new()\n            .route(\"ci.\", \"sykli\")\n            .route(\"kernel.\", \"tapio\"))\n        .emitter(grpc_downstream))\n}).await\n```\n\n### FP Components\n\n| Component | What it does |\n|-----------|--------------|\n| `OccurrenceIngestor` | Decode Occurrence JSON (single or array) into Messages |\n| `OccurrenceRouter` | Route by `message_type` prefix (first match wins) |\n| `OccurrenceCorrelator` | Track correlation groups, enrich with `polku.correlation_key` metadata |\n| `OccurrenceAggregator` | Batch correlated messages into summaries with History linking |\n| `Probe` | Self-aware pipeline observer — emits Occurrences about pressure, health, circuit state |\n\n### History \u0026 Causal Chains\n\nOccurrences carry a `History` block for causal chain tracking:\n\n```json\n{\n  \"type\": \"gateway.pipeline.pressure_recovered\",\n  \"history\": {\n    \"previous_ids\": [\"01ALERT_ID\"],\n    \"lifecycle\": \"resolved\"\n  },\n  \"context\": {\n    \"correlation_keys\": [\"probe.pressure_high.polku-prod\"]\n  }\n}\n```\n\nThe Probe automatically links recovery events to their triggering alerts via shared correlation keys and `history.previous_ids`. The Aggregator marks batch summaries with `lifecycle: \"aggregated\"` and links to all constituent IDs.\n\n---\n\n## Write a Plugin\n\nPlugins communicate over gRPC. Write them in any language.\n\n### Python: Slack Emitter\n\n```python\n# Receives Message data as Event over gRPC\nclass SlackEmitter:\n    def Info(self, request, context):\n        return PluginInfo(\n            name=\"slack-emitter\",\n            type=EMITTER,\n            emitter_name=\"slack\",\n        )\n\n    def Emit(self, request, context):\n        for event in request.events:\n            message = f\"*{event.event_type}* from `{event.source}`\"\n            requests.post(SLACK_WEBHOOK, json={\"text\": message})\n        return EmitResponse(success_count=len(request.events))\n\nserver = grpc.server(ThreadPoolExecutor())\nserver.add_insecure_port('[::]:9001')\nserver.start()\n```\n\n### Go: CSV Ingestor\n\n```go\nfunc (p *CSVIngestor) Ingest(ctx context.Context, req *IngestRequest) (*IngestResponse, error) {\n    reader := csv.NewReader(bytes.NewReader(req.Data))\n    headers, _ := reader.Read()\n\n    var events []*Event\n    for {\n        row, err := reader.Read()\n        if err != nil { break }\n\n        metadata := make(map[string]string)\n        for i, h := range headers {\n            metadata[h] = row[i]\n        }\n\n        events = append(events, \u0026Event{\n            Id:        uuid.New().String(),\n            EventType: \"csv.row\",\n            Metadata:  metadata,\n        })\n    }\n    return \u0026IngestResponse{Events: events}, nil\n}\n```\n\nWire up in Rust:\n```rust\nHub::new()\n    .ingestor(ExternalIngestor::new(\"csv\", \"http://localhost:9002\"))\n    .emitter(Arc::new(ExternalEmitter::new(\"http://localhost:9001\")))\n    .build();\n```\n\n---\n\n## Configuration\n\n### Environment Variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `POLKU_GRPC_ADDR` | `[::1]:50051` | gRPC listen address |\n| `POLKU_METRICS_ADDR` | `127.0.0.1:9090` | Prometheus metrics address |\n| `POLKU_BUFFER_CAPACITY` | `100000` | Ring buffer capacity |\n| `POLKU_BATCH_SIZE` | `1000` | Batch size for emitters |\n| `POLKU_FLUSH_INTERVAL_MS` | `100` | Flush interval (ms) |\n| `POLKU_LOG_LEVEL` | `info` | Log level |\n| `POLKU_LOG_FORMAT` | `pretty` | Log format (`pretty` or `json`) |\n| `POLKU_EMIT_GRPC_ENDPOINTS` | - | Comma-separated gRPC downstream endpoints |\n| `POLKU_EMIT_GRPC_LAZY` | `false` | Lazy gRPC connections |\n\nSee [Usage](#usage) for programmatic Rust examples.\n\n---\n\n## Observability\n\n### Prometheus Metrics (port 9090)\n\nPOLKU exposes 40+ metrics at `GET /metrics`:\n\n| Category | Key Metrics |\n|----------|-------------|\n| **Throughput** | `polku_events_received_total`, `polku_events_forwarded_total`, `polku_events_per_second` |\n| **Buffer** | `polku_buffer_size`, `polku_buffer_capacity`, `polku_buffer_overflow_total` |\n| **Latency** | `polku_processing_latency`, `polku_flush_duration_seconds`, `polku_middleware_duration_seconds` |\n| **Emitter health** | `polku_emitter_health`, `polku_circuit_breaker_state`, `polku_emitter_throughput` |\n| **Load balancing** | `polku_grpc_endpoint_fill_ratio`, `polku_grpc_endpoint_health`, `polku_grpc_failover_total` |\n| **Pipeline** | `polku_pipeline_pressure` (composite: 0.0 idle → 1.0 overloaded) |\n\n### Health Endpoints\n\n- `GET /health` - JSON health summary with pipeline pressure and per-component status\n- `GET /pipeline` - JSON pipeline manifest (topology, components, configuration)\n\n### Processing Trace\n\nSet `metadata[\"polku.trace\"] = \"true\"` on a message to get per-middleware timing:\n\n```json\n[{\"mw\":\"filter\",\"action\":\"passed\",\"us\":12},{\"mw\":\"router\",\"action\":\"routed\",\"targets\":[\"kafka\"]}]\n```\n\nStored in `metadata[\"_polku.trace\"]` after pipeline processing.\n\n---\n\n## Performance\n\n| Metric | Value |\n|--------|-------|\n| Memory | 10-20 MB |\n| Streaming throughput | 178k+ events/sec |\n| Unary throughput | ~1.3k events/sec |\n| Plugin overhead | ~100-500us per gRPC call |\n\nStreaming (`StreamEvents`) is the high-throughput path. For bulk ingestion, use batched streaming instead of unary `SendEvent`.\n\nThe pipeline is zero-copy end-to-end: `Bytes` payloads flow without allocation, `InternedStr` fields clone in O(1), and `MessageId` is `Copy`.\n\n---\n\n## Project Structure\n\n```\npolku/\n├── core/                    # Shared types crate\n│   └── src/\n│       ├── message.rs       # Message, MessageId, Routes, Metadata\n│       ├── emit.rs          # Emitter trait\n│       ├── error.rs         # PluginError\n│       └── intern.rs        # InternedStr\n│\n├── gateway/                 # Gateway library + binary\n│   └── src/\n│       ├── main.rs          # Standalone entry point\n│       ├── config.rs        # Env var configuration\n│       ├── server.rs        # gRPC server (polku.v1.Gateway)\n│       ├── hub/             # Hub builder + runner\n│       ├── ingest/          # Ingestors (3 built-in)\n│       ├── middleware/       # Middleware (10 types)\n│       ├── emit/            # Emitters (4 + resilience wrappers)\n│       ├── metrics.rs       # Prometheus metric definitions\n│       ├── metrics_server.rs# /metrics, /health, /pipeline\n│       ├── buffer.rs        # RingBuffer\n│       ├── buffer_tiered.rs # TieredBuffer (zstd compression)\n│       └── manifest.rs      # Pipeline self-description\n│\n├── false-protocol/          # FALSE Protocol types (standalone)\n│   └── src/\n│       └── lib.rs           # Occurrence, Severity, Outcome, History, Context, Error, Reasoning\n│\n├── fp/                      # FALSE Protocol plugins for POLKU\n│   └── src/\n│       ├── ingest.rs        # OccurrenceIngestor — JSON → Message\n│       ├── middleware.rs     # OccurrenceRouter — prefix-based routing\n│       ├── correlator.rs    # OccurrenceCorrelator — correlation key enrichment\n│       ├── aggregator.rs    # OccurrenceAggregator — batch by correlation key\n│       └── probe.rs         # Probe — self-aware pipeline observer\n│\n├── runtime/                 # Injectable pipeline interface\n│   └── src/\n│       ├── lib.rs           # run(), RuntimeBuilder\n│       └── prelude.rs       # Re-exports for pipeline authors\n│\n├── ci/                      # CI utilities\n├── test-plugins/            # Test plugin implementations\n│   └── receiver/            # gRPC receiver for testing\n├── tests/e2e/               # End-to-end tests (Kind k8s)\n└── docs/                    # Documentation\n```\n\nProto definitions live in a [separate repository](https://github.com/false-systems/proto) at `proto/polku/v1/`:\n- `gateway.proto` - Client-facing gRPC service (`SendEvent`, `StreamEvents`, `Health`)\n- `plugin.proto` - Plugin gRPC interface (Ingestor/Emitter plugins)\n- `event.proto` - Wire-format Event type (gRPC boundaries only)\n\n---\n\n## Naming\n\n**Polku** (Finnish) = \"path\"\n\nThe path your messages take through the system.\n\n---\n\n## License\n\nApache 2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffalse-systems%2Fpolku","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffalse-systems%2Fpolku","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffalse-systems%2Fpolku/lists"}