Skip to content

Add RAS topology macro for compile-checked service graphs and generated artifacts #15

@JedimEmO

Description

@JedimEmO

Summary

Add a RAS topology macro/crate for compile-checked multi-service graphs, generated auth/gateway artifacts, and diagrams.

RAS already uses macros to declare service contracts and generate runtime glue, clients, specs, and permission manifests. A topology crate should apply the same philosophy one level higher: declare the logical service graph, route graph, gateway profiles, audiences, and allowed service-to-service edges using typed references to service API crates and generated permission constants.

The topology must remain deployment-substrate agnostic. It should describe the auth and communication intent, while Kubernetes, Docker Compose, systemd, Nomad, IPC process managers, service meshes, DNS, or static config provide concrete runtime resolution.

Related:

Problem

Once RAS supports internal service identity, auth gateways, and service-to-service tokens, hand-written route/audience/service graph config becomes risky:

  • gateway route config can drift from actual service APIs
  • service-to-service grants can refer to deleted or renamed permissions
  • public gateways may accidentally expose private services
  • multiple gateways need different route sets and trust profiles
  • diagrams and docs can drift from runtime config
  • topology permissions are hard to validate if they are just strings
  • deployment systems differ, but the logical graph should stay stable

The topology crate should make multi-service RAS systems explicit and checkable without forcing one deployment platform.

Proposed Direction

Add a new topology crate family, likely under crates/topology/:

  • ras-topology-core
    • topology model types for services, audiences, routes, gateways, edges, exposures, upstream references, and artifacts
    • validation helpers
    • serializers for generated policy/config/diagram artifacts
  • ras-topology-macro
    • ras_topology! macro for declaring the logical graph
    • compile-time checks where Rust types/constants can prove correctness
    • generated constants and artifact emitters
  • optional deployment artifact modules later:
    • static gateway config
    • Docker Compose snippets
    • Kubernetes ConfigMap/Ingress snippets
    • IPC/process-manager snippets
    • Mermaid/DOT diagrams

The topology crate should usually be a downstream crate that depends on service API crates:

invoice-api
billing-api
admin-api
  -> internal-tools-topology
      -> imports service permission manifests/constants
      -> emits authz seed/policy artifacts for #13
      -> emits gateway profiles/config for #14
      -> emits diagrams/docs

Macro Sketch

ras_topology!({
    topology_name: InternalTools,

    services: [
        invoice: {
            audience: "invoice-service",
            manifest: invoice_api::generate_invoiceservice_permission_manifest,
            exposure: private,
        },
        billing: {
            audience: "billing-service",
            manifest: billing_api::generate_billingservice_permission_manifest,
            exposure: private,
        },
        admin: {
            audience: "admin-service",
            manifest: admin_api::generate_adminservice_permission_manifest,
            exposure: private,
        },
    ],

    gateways: [
        public_web: {
            exposure: public,
            routes: [
                "/invoices" => invoice,
                "/billing" => billing,
            ],
        },
        private_admin: {
            exposure: private,
            routes: [
                "/admin" => admin,
                "/internal/invoices" => invoice,
            ],
        },
    ],

    calls: [
        billing -> invoice {
            permissions: [
                invoice_api::invoiceservice_permissions::INVOICE_READ,
                invoice_api::invoiceservice_permissions::INVOICE_WRITE,
            ],
        },
    ],
});

The exact syntax can evolve, but the key design is typed references to service manifests and generated permission constants, not raw string-only topology.

Validation Levels And Permission Typing

The topology design should be explicit about which guarantees are compile-time, build-time, and runtime/startup validation.

Expected split:

  • compile-time: syntax, duplicate service IDs inside macro input, references to Rust symbols that must exist, and any checks made possible by typed generated permission references
  • build-time/test-time: deterministic artifact generation, service manifest import, route conflict validation, exposure checks, and manifest-known permission checks
  • runtime/startup: deployment-provided upstream bindings, resolver compatibility, missing upstreams, and environment-specific validation

If topology wants to prove that an edge permission belongs to the target service at compile time, generated permission constants likely need to become service-typed, for example PermissionRef<InvoiceService>, or expose equivalent target-service metadata. If existing PermissionRef remains an untyped string wrapper, the issue should treat target-service membership as build-time validation against imported manifests rather than promising full compile-time proof.

Generated Artifacts

The topology crate should generate or expose:

All generated artifacts should include schema version, topology name, deterministic topology/artifact IDs, and enough source metadata to make CI diffs and authorization audits meaningful.

Examples:

target/ras-topology/internal-tools.authz.json
target/ras-topology/public_web.gateway.toml
target/ras-topology/private_admin.gateway.toml
target/ras-topology/internal-tools.mermaid

Deployment Boundary

RAS topology should dictate auth topology, not infrastructure scheduling.

RAS topology owns:

  • services and audiences
  • permission manifests
  • allowed service-to-service edges
  • gateway profiles
  • route-to-audience mappings
  • exposure class
  • generated policy/config/diagram artifacts

RAS topology does not own in v1:

  • Kubernetes vs Docker Compose vs Nomad vs systemd
  • ingress controller choice
  • DNS implementation
  • service mesh
  • certificate provisioning
  • load-balancing policy beyond configured upstream references
  • rollout/deployment lifecycle

Concrete upstream resolution should be deployment-specific:

[upstreams.invoice-service]
url = "http://invoice-service.default.svc.cluster.local:3000"

or:

[upstreams.invoice-service]
url = "unix:///run/ras/invoice.sock"

or embedded/in-process routing in a single binary adapter.

Enforcement Model

Because the topology crate is downstream of service API crates, services do not directly depend on it by default.

Enforcement should work through generated authority/gateway artifacts:

  1. Service APIs declare inbound permissions through existing RAS macros.
  2. Service API crates emit permission manifests/constants.
  3. Topology crate imports those manifests/constants and declares allowed graph edges.
  4. Add RAS-native authorization control plane and internal service token issuer #13 auth authority loads topology policy and refuses to mint service tokens outside the graph.
  5. Add optional RAS auth gateway for multi-service browser frontends #14 gateway loads topology route/audience config and only narrows tokens for declared routes/audiences.
  6. Target services still enforce their own generated RAS permissions.

Optional defense-in-depth may allow services to load topology policy artifacts and reject callers/edges locally, but this should not be required for v1.

Multiple Gateways

The topology must support multiple gateway profiles:

  • public web gateway
  • private/admin gateway
  • internal API gateway
  • partner/customer gateway

Each profile should generate separate route/audience artifacts and diagrams.

Validation must catch unsafe exposure:

  • public gateway exposing a private-only service unless explicitly allowed
  • route conflicts within one gateway profile
  • gateway route pointing to an undeclared service
  • gateway profile using an unsupported upstream resolver

Route conflicts across different gateway profiles are allowed.

Security Requirements

  • Audiences must be unique within a topology.
  • Gateway routes must map only to declared services.
  • Service-to-service edges must reference declared source/target services.
  • Edge permissions must reference target service permission constants or manifest-known permissions.
  • The issue must state whether target-service permission membership is compile-time checked through service-typed permission refs or build-time checked against imported manifests.
  • Permission-string fallback should be explicit and marked as custom/manual.
  • Public/private exposure mismatches should fail validation by default.
  • Topology-generated Add RAS-native authorization control plane and internal service token issuer #13 policy must be treated as authorization input and versioned.
  • Topology-generated Add optional RAS auth gateway for multi-service browser frontends #14 gateway config must bind route, audience, exposure, and upstream logical name together.
  • Generated authz, gateway, diagram, and validation artifacts must carry schema versions and deterministic IDs.
  • Generated diagrams/config must not include secrets.
  • Deployment adapters must not hide failed validation.

Acceptance Criteria

  • A downstream topology crate can import multiple service API crates and their generated permission manifests.
  • The topology macro validates unique service IDs and audiences.
  • The topology macro validates gateway routes reference declared services.
  • The topology macro validates service-to-service calls reference declared services.
  • Service-to-service edge permissions can use generated permission constants from target service API crates.
  • Target-service membership for edge permissions is either compile-checked through service-typed permission refs or validated deterministically against imported manifests.
  • The topology can emit a Add RAS-native authorization control plane and internal service token issuer #13 authorization policy/seed artifact containing allowed service graph edges and manifest-known permissions.
  • The topology can emit Add optional RAS auth gateway for multi-service browser frontends #14 gateway config artifacts per gateway profile.
  • Multiple gateway profiles are supported.
  • Exposure validation prevents accidental public exposure of private services by default.
  • Mermaid or DOT diagrams can be generated from the same topology.
  • Topology artifacts are deterministic for CI diffs.
  • Topology artifacts include schema versions and stable IDs suitable for audit trails.
  • Runtime/startup validation hooks can verify deployment-provided upstreams resolve to known logical service IDs, without RAS owning DNS or scheduling.

Test Plan

  • Compile-fail tests for duplicate audiences, unknown services, unknown gateway route targets, and invalid edge references.
  • Tests for typed permission constant references from service API crates.
  • Tests documenting the chosen target-service permission membership model: compile-fail when service-typed refs are used, or deterministic validation failure when imported-manifest validation is used.
  • Tests for explicit custom/manual permission fallback.
  • Tests for multiple gateway profiles and route conflicts scoped per profile.
  • Tests for public gateway exposing private service failure.
  • Tests for deterministic authz/gateway/diagram artifact generation.
  • Tests for Kubernetes-style URL, Docker Compose-style URL, Unix socket, and embedded upstream reference serialization without requiring those environments.
  • Tests showing Add RAS-native authorization control plane and internal service token issuer #13 can consume generated topology policy.
  • Tests showing Add optional RAS auth gateway for multi-service browser frontends #14 can consume generated gateway profile config.

Non-Goals For v1

  • Do not require every RAS app to use a topology crate.
  • Do not replace single-service embedded auth mode.
  • Do not implement a service mesh.
  • Do not require Kubernetes or any specific scheduler.
  • Do not perform live network discovery in the proc macro.
  • Do not force services to depend on the downstream topology crate.

Notes

This keeps RAS philosophically aligned with its current design: declare intent in Rust, use macros for compile-time verification where possible, generate runtime glue/artifacts, and keep deployment-specific choices pluggable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions