[{"data":1,"prerenderedAt":4651},["ShallowReactive",2],{"search-sections-aegis":3,"nav-aegis":908,"content-tree-aegis":950,"footer-resources":969,"content-/v0.0.4/reference/api":2552,"surround-/v0.0.4/reference/api":4648},[4,10,14,20,25,30,35,40,44,49,54,59,64,69,74,78,83,87,92,97,102,107,112,117,122,126,131,135,140,145,151,156,161,165,170,175,180,184,189,194,199,204,209,213,218,222,227,232,237,242,247,252,257,262,267,271,276,280,284,289,294,299,303,308,313,317,322,327,331,336,341,345,350,354,359,364,369,373,378,382,387,392,397,401,406,411,416,420,425,430,435,440,445,450,454,459,464,469,473,478,482,487,491,496,501,506,511,515,520,525,530,534,539,544,549,553,558,563,568,572,577,581,585,590,595,600,605,610,615,620,625,630,635,638,643,648,653,658,663,668,673,678,683,688,693,698,703,706,711,716,721,726,731,736,741,744,749,754,759,763,768,773,777,782,787,791,796,801,805,810,815,819,824,828,832,837,842,847,852,856,860,864,869,874,879,884,889,894,899,904],{"id":5,"title":6,"titles":7,"content":8,"level":9},"/v0.0.4/learn/overview","Overview",[],"Service mesh for Go microservices with automatic mTLS",1,{"id":11,"title":6,"titles":12,"content":13,"level":9},"/v0.0.4/learn/overview#overview",[],"Aegis is a service mesh for Go microservices. Nodes discover each other, authenticate via mTLS, and call domain services—all without managing PKI infrastructure.",{"id":15,"title":16,"titles":17,"content":18,"level":19},"/v0.0.4/learn/overview#the-idea","The Idea",[6],"Microservices need secure communication. Traditional approaches require certificate authorities, key distribution, and rotation policies. Aegis asks: what if nodes could establish trust automatically? When a node starts, it generates certificates. When nodes connect, they verify each other. The mesh handles topology—which nodes exist, which services they provide, where to route requests. You write domain logic; aegis handles transport security.",2,{"id":21,"title":22,"titles":23,"content":24,"level":19},"/v0.0.4/learn/overview#the-implementation","The Implementation",[6],"Aegis provides: Node — Identity, lifecycle, and server managementPeer — Connections to other nodes with mTLSTopology — Distributed view of mesh membershipService Registry — Declare services, discover providersService Client — Connection pooling and load balancingHealth — Extensible health checking",{"id":26,"title":27,"titles":28,"content":29,"level":19},"/v0.0.4/learn/overview#what-it-enables","What It Enables",[6],"Build distributed systems where: Services call each other without hardcoded addressesEvery connection is authenticated and encryptedTopology changes propagate automaticallyCallers are identified on every request Aegis is the transport layer for the zoobzio ecosystem: PackageRolemorpheusIdentity servicevickyStorage servicecapitanEvent coordinationheraldMessage broker bridge",{"id":31,"title":32,"titles":33,"content":34,"level":19},"/v0.0.4/learn/overview#next-steps","Next Steps",[6],"Quickstart — Build your first provider and consumerConcepts — Understand nodes, peers, and servicesAPI Reference — Function signatures and usage",{"id":36,"title":37,"titles":38,"content":39,"level":9},"/v0.0.4/learn/quickstart","Quickstart",[],"Build a provider and consumer in five minutes",{"id":41,"title":37,"titles":42,"content":43,"level":9},"/v0.0.4/learn/quickstart#quickstart",[],"This guide walks through creating two nodes: a provider that exposes a service, and a consumer that calls it.",{"id":45,"title":46,"titles":47,"content":48,"level":19},"/v0.0.4/learn/quickstart#installation","Installation",[37],"go get github.com/zoobz-io/aegis Requires Go 1.24+.",{"id":50,"title":51,"titles":52,"content":53,"level":19},"/v0.0.4/learn/quickstart#basic-usage","Basic Usage",[37],"A minimal node with automatic certificate generation: package main\n\nimport \"github.com/zoobz-io/aegis\"\n\nfunc main() {\n    node, err := aegis.NewNodeBuilder().\n        WithID(\"my-node\").\n        WithName(\"My Node\").\n        WithAddress(\"localhost:8443\").\n        WithCertDir(\"./certs\").\n        Build()\n    if err != nil {\n        panic(err)\n    }\n\n    node.StartServer()\n    defer node.Shutdown()\n\n    // Node is now accepting mTLS connections\n    select {}\n} On first run, aegis generates a CA and node certificate in ./certs. Subsequent runs load existing certificates.",{"id":55,"title":56,"titles":57,"content":58,"level":19},"/v0.0.4/learn/quickstart#declaring-services","Declaring Services",[37],"Nodes can declare which services they provide: node, _ := aegis.NewNodeBuilder().\n    WithID(\"identity-1\").\n    WithName(\"Identity Server\").\n    WithAddress(\"localhost:8443\").\n    WithServices(aegis.ServiceInfo{Name: \"identity\", Version: \"v1\"}).\n    WithCertDir(\"./certs\").\n    Build() Other nodes can then query for providers: providers := node.Topology.GetServiceProviders(\"identity\", \"v1\")\n// Returns []NodeInfo with addresses of nodes providing identity.v1 See the Services Guide for registering gRPC service implementations.",{"id":60,"title":61,"titles":62,"content":63,"level":19},"/v0.0.4/learn/quickstart#connecting-to-peers","Connecting to Peers",[37],"Add peers to enable topology synchronization: err := node.AddPeer(aegis.PeerInfo{\n    ID:      \"other-node\",\n    Type:    aegis.NodeTypeGeneric,\n    Address: \"other-host:8443\",\n}) Peers connect using mTLS. Once connected, topology syncs automatically.",{"id":65,"title":66,"titles":67,"content":68,"level":19},"/v0.0.4/learn/quickstart#calling-services","Calling Services",[37],"Use ServiceClientPool to call services across the mesh: pool := aegis.NewServiceClientPool(node)\ndefer pool.Close()\n\n// Create typed client for a specific service\nclient := aegis.NewServiceClient(pool, \"identity\", \"v1\", identity.NewIdentityServiceClient)\n\n// Get connection (round-robin across providers)\nidentityClient, err := client.Get(ctx)\nif err != nil {\n    return err\n}\n\nresp, err := identityClient.ValidateSession(ctx, &identity.ValidateSessionRequest{\n    Token: token,\n}) See Service Client for connection pooling details.",{"id":70,"title":71,"titles":72,"content":73,"level":19},"/v0.0.4/learn/quickstart#health-checks","Health Checks",[37],"Nodes track their health status: // Set health directly\nnode.SetHealth(aegis.HealthStatusHealthy, \"All systems operational\", nil)\n\n// Or use a health checker\nchecker := aegis.NewFunctionHealthChecker(\"database\", func(ctx context.Context) error {\n    return db.Ping(ctx)\n})\nnode.CheckHealth(ctx, checker)",{"id":75,"title":32,"titles":76,"content":77,"level":19},"/v0.0.4/learn/quickstart#next-steps",[37],"Concepts — Mental models for nodes, peers, and topologyArchitecture — How mTLS and topology sync work internallyServices Guide — Registering domain services html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}",{"id":79,"title":80,"titles":81,"content":82,"level":9},"/v0.0.4/learn/concepts","Concepts",[],"Mental models for nodes, peers, topology, and services",{"id":84,"title":80,"titles":85,"content":86,"level":9},"/v0.0.4/learn/concepts#concepts",[],"This page explains the core abstractions in aegis and how they relate to each other.",{"id":88,"title":89,"titles":90,"content":91,"level":19},"/v0.0.4/learn/concepts#node","Node",[80],"A Node is a participant in the mesh. It has: ID — Unique identifier, used in certificates and topologyName — Human-readable labelType — Classification (e.g., NodeTypeGeneric)Address — Host and port for connectionsServices — List of services this node provides node, _ := aegis.NewNodeBuilder().\n    WithID(\"api-1\").\n    WithName(\"API Server\").\n    WithAddress(\"localhost:8443\").\n    WithServices(aegis.ServiceInfo{Name: \"identity\", Version: \"v1\"}).\n    Build() A node manages its own: MeshServer — gRPC server for incoming connectionsPeerManager — Outgoing connections to other nodesTopology — View of all nodes in the meshTLSConfig — Certificates for mTLS Think of a node as a process's identity in the mesh.",{"id":93,"title":94,"titles":95,"content":96,"level":19},"/v0.0.4/learn/concepts#peer","Peer",[80],"A Peer is a connection to another node. The PeerManager maintains these connections. // Add a peer\nnode.AddPeer(aegis.PeerInfo{\n    ID:      \"other-node\",\n    Type:    aegis.NodeTypeGeneric,\n    Address: \"other-host:8443\",\n})\n\n// Get all peers\npeers := node.GetAllPeers()\n\n// Communicate with a peer\nresp, _ := node.PingPeer(ctx, \"other-node\") Peers are directional: if node A adds node B as a peer, A initiates the connection. For bidirectional communication, both nodes should add each other. All peer connections use mTLS. The certificates verify node identity.",{"id":98,"title":99,"titles":100,"content":101,"level":19},"/v0.0.4/learn/concepts#topology","Topology",[80],"The Topology is a shared view of mesh membership. Each node maintains its own copy, synchronized via gossip. // Add a node to local topology\nnode.Topology.AddNode(aegis.NodeInfo{\n    ID:      \"new-node\",\n    Name:    \"New Node\",\n    Address: \"new-host:8443\",\n})\n\n// Query topology\nallNodes := node.Topology.GetAllNodes()\nserviceProviders := node.Topology.GetServiceProviders(\"identity\", \"v1\") Topology has a version number. When nodes sync, the higher version wins. This provides eventual consistency—all nodes converge to the same view.",{"id":103,"title":104,"titles":105,"content":106,"level":19},"/v0.0.4/learn/concepts#service","Service",[80],"A Service is a capability that a node provides. Services have a name and version: aegis.ServiceInfo{Name: \"identity\", Version: \"v1\"} Services are declared when building a node: node, _ := aegis.NewNodeBuilder().\n    WithServices(\n        aegis.ServiceInfo{Name: \"identity\", Version: \"v1\"},\n        aegis.ServiceInfo{Name: \"audit\", Version: \"v1\"},\n    ).\n    Build() The service registry answers: \"Which nodes provide service X?\" providers := node.Topology.GetServiceProviders(\"identity\", \"v1\") This enables location-transparent service calls. Consumers don't need to know specific addresses.",{"id":108,"title":109,"titles":110,"content":111,"level":19},"/v0.0.4/learn/concepts#serviceclientpool","ServiceClientPool",[80],"The ServiceClientPool manages connections to service providers: pool := aegis.NewServiceClientPool(node)\ndefer pool.Close() It provides: Connection pooling — Reuses connections to the same addressRound-robin — Distributes calls across providersmTLS — All connections are authenticated Create typed clients for specific services: client := aegis.NewServiceClient(pool, \"identity\", \"v1\", identity.NewIdentityServiceClient)\n\n// Get a client (may connect to different provider each time)\nidentityClient, _ := client.Get(ctx)",{"id":113,"title":114,"titles":115,"content":116,"level":19},"/v0.0.4/learn/concepts#caller","Caller",[80],"A Caller represents the identity of a node making a request. Extract it from the gRPC context: func (s *MyServer) HandleRequest(ctx context.Context, req *pb.Request) (*pb.Response, error) {\n    caller, err := aegis.CallerFromContext(ctx)\n    if err != nil {\n        return nil, err\n    }\n\n    log.Printf(\"Request from node: %s\", caller.NodeID)\n    // caller.Certificate contains the full X.509 certificate\n} This enables authorization decisions based on caller identity.",{"id":118,"title":119,"titles":120,"content":121,"level":19},"/v0.0.4/learn/concepts#how-they-relate","How They Relate",[80],"┌─────────────────────────────────────────────────────────────┐\n│                            Node                             │\n│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │\n│  │ MeshServer  │  │ PeerManager │  │      Topology       │  │\n│  │  (inbound)  │  │  (outbound) │  │  (mesh membership)  │  │\n│  └──────┬──────┘  └──────┬──────┘  └──────────┬──────────┘  │\n│         │                │                    │              │\n│         │    mTLS        │    mTLS            │ sync         │\n│         ▼                ▼                    ▼              │\n│  ┌───────────────────────────────────────────────────────┐  │\n│  │                       TLSConfig                       │  │\n│  │           (certificates, CA, verification)            │  │\n│  └───────────────────────────────────────────────────────┘  │\n└─────────────────────────────────────────────────────────────┘\n                              │\n                              │ Services declared\n                              ▼\n                    ┌───────────────────┐\n                    │  ServiceRegistry  │\n                    │   (in Topology)   │\n                    └─────────┬─────────┘\n                              │\n                              │ GetServiceProviders()\n                              ▼\n                    ┌───────────────────┐\n                    │ ServiceClientPool │\n                    │   (connections)   │\n                    └───────────────────┘",{"id":123,"title":32,"titles":124,"content":125,"level":19},"/v0.0.4/learn/concepts#next-steps",[80],"Architecture — How mTLS and topology sync work internallyTypes Reference — Full type definitions html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}",{"id":127,"title":128,"titles":129,"content":130,"level":9},"/v0.0.4/learn/architecture","Architecture",[],"How mTLS, topology sync, and service discovery work internally",{"id":132,"title":128,"titles":133,"content":134,"level":9},"/v0.0.4/learn/architecture#architecture",[],"This document explains how aegis works internally. It's intended for contributors and users who want to understand the implementation.",{"id":136,"title":137,"titles":138,"content":139,"level":19},"/v0.0.4/learn/architecture#component-overview","Component Overview",[128],"┌─────────────────────────────────────────────────────────────────────┐\n│                              Node                                    │\n├─────────────────────────────────────────────────────────────────────┤\n│                                                                      │\n│  ┌──────────────┐    ┌──────────────┐    ┌────────────────────┐    │\n│  │  MeshServer  │    │ PeerManager  │    │     Topology       │    │\n│  │              │    │              │    │                    │    │\n│  │  - Ping      │    │  - AddPeer   │    │  - AddNode         │    │\n│  │  - Health    │    │  - Connect   │    │  - RemoveNode      │    │\n│  │  - NodeInfo  │    │  - PingPeer  │    │  - GetProviders    │    │\n│  │  - Topology  │    │  - SyncTopo  │    │  - Merge           │    │\n│  │  - Services  │    │              │    │                    │    │\n│  └──────┬───────┘    └──────┬───────┘    └─────────┬──────────┘    │\n│         │                   │                      │               │\n│         │ gRPC/mTLS         │ gRPC/mTLS           │ version-based │\n│         ▼                   ▼                      ▼               │\n│  ┌─────────────────────────────────────────────────────────────┐   │\n│  │                        TLSConfig                             │   │\n│  │                                                              │   │\n│  │  - CA certificate (trust root)                               │   │\n│  │  - Node certificate (identity)                               │   │\n│  │  - Private key                                               │   │\n│  │  - Server config (require client cert)                       │   │\n│  │  - Client config (verify server cert)                        │   │\n│  └─────────────────────────────────────────────────────────────┘   │\n│                                                                      │\n└─────────────────────────────────────────────────────────────────────┘",{"id":141,"title":142,"titles":143,"content":144,"level":19},"/v0.0.4/learn/architecture#mtls-flow","mTLS Flow",[128],"",{"id":146,"title":147,"titles":148,"content":149,"level":150},"/v0.0.4/learn/architecture#certificate-generation","Certificate Generation",[128,142],"When a node starts with WithCertDir(), aegis checks for existing certificates: CA exists? Load ca-cert.pem and ca-key.pemCA missing? Generate new CA (4096-bit RSA, 365-day validity)Node cert exists? Load {nodeID}-cert.pem and {nodeID}-key.pemNode cert missing? Generate using CA (2048-bit RSA, 90-day validity) certs/\n├── ca-cert.pem     # CA certificate (shared across nodes)\n├── ca-key.pem      # CA private key (keep secure)\n├── node-1-cert.pem # Node certificate\n└── node-1-key.pem  # Node private key For production, provide your own CA via WithTLSOptions().",3,{"id":152,"title":153,"titles":154,"content":155,"level":150},"/v0.0.4/learn/architecture#connection-establishment","Connection Establishment",[128,142],"Server side (MeshServer): tlsConfig := &tls.Config{\n    ClientAuth: tls.RequireAndVerifyClientCert,\n    ClientCAs:  certPool,  // CA that signed client certs\n    Certificates: []tls.Certificate{serverCert},\n} Client side (PeerManager): tlsConfig := &tls.Config{\n    RootCAs:      certPool,  // CA that signed server cert\n    Certificates: []tls.Certificate{clientCert},\n    ServerName:   peerAddress,\n} Both sides verify certificates against the CA. Connection fails if: Certificate not signed by trusted CACertificate expiredCommon Name doesn't match expected identity",{"id":157,"title":158,"titles":159,"content":160,"level":150},"/v0.0.4/learn/architecture#caller-extraction","Caller Extraction",[128,142],"On every request, the server can extract caller identity: caller, _ := aegis.CallerFromContext(ctx)\n// caller.NodeID = certificate Common Name\n// caller.Certificate = full X.509 certificate This uses gRPC's peer.FromContext() to access TLS state.",{"id":162,"title":163,"titles":164,"content":144,"level":19},"/v0.0.4/learn/architecture#topology-synchronization","Topology Synchronization",[128],{"id":166,"title":167,"titles":168,"content":169,"level":150},"/v0.0.4/learn/architecture#data-model","Data Model",[128,163],"type Topology struct {\n    Nodes     map[string]NodeInfo  // nodeID → info\n    Version   int64                 // monotonically increasing\n    UpdatedAt time.Time\n}\n\ntype NodeInfo struct {\n    ID        string\n    Name      string\n    Type      NodeType\n    Address   string\n    Services  []ServiceInfo\n    JoinedAt  time.Time\n    UpdatedAt time.Time\n}",{"id":171,"title":172,"titles":173,"content":174,"level":150},"/v0.0.4/learn/architecture#sync-protocol","Sync Protocol",[128,163],"When node A syncs with node B: A calls B.SyncTopology(version: A.version)B responds with its full topology and versionIf B.version > A.version, A adopts B's topology if resp.Version > n.Topology.GetVersion() {\n    n.Topology.Merge(remoteTopology)\n} This is a simple \"highest version wins\" model. More sophisticated CRDT-based merging could be added for conflict resolution.",{"id":176,"title":177,"titles":178,"content":179,"level":150},"/v0.0.4/learn/architecture#when-sync-happens","When Sync Happens",[128,163],"Manually via node.SyncTopology(ctx, peerID)Bulk via node.SyncTopologyWithAllPeers(ctx)Applications can trigger on timers or events",{"id":181,"title":182,"titles":183,"content":144,"level":19},"/v0.0.4/learn/architecture#service-discovery","Service Discovery",[128],{"id":185,"title":186,"titles":187,"content":188,"level":150},"/v0.0.4/learn/architecture#registration","Registration",[128,182],"Services are declared at node creation: WithServices(aegis.ServiceInfo{Name: \"identity\", Version: \"v1\"}) This adds services to the node's NodeInfo, which propagates via topology sync.",{"id":190,"title":191,"titles":192,"content":193,"level":150},"/v0.0.4/learn/architecture#discovery","Discovery",[128,182],"Query topology for providers: providers := topology.GetServiceProviders(\"identity\", \"v1\") This scans all nodes, returning those with matching service declarations: func (t *Topology) GetServiceProviders(name, version string) []NodeInfo {\n    var providers []NodeInfo\n    for _, node := range t.Nodes {\n        for _, svc := range node.Services {\n            if svc.Name == name && svc.Version == version {\n                providers = append(providers, node)\n                break\n            }\n        }\n    }\n    return providers\n}",{"id":195,"title":196,"titles":197,"content":198,"level":150},"/v0.0.4/learn/architecture#client-load-balancing","Client Load Balancing",[128,182],"ServiceClientPool distributes calls via round-robin: type ServiceClientPool struct {\n    conns    map[string]*grpc.ClientConn  // address → connection\n    counters map[string]*atomic.Uint64     // service → counter\n}\n\nfunc (p *ServiceClientPool) GetConn(ctx context.Context, name, version string) (*grpc.ClientConn, error) {\n    providers := p.node.Topology.GetServiceProviders(name, version)\n    idx := p.counters[key].Add(1) - 1\n    provider := providers[idx % uint64(len(providers))]\n    return p.getOrCreateConn(provider.Address)\n} Connections are pooled and reused across calls.",{"id":200,"title":201,"titles":202,"content":203,"level":19},"/v0.0.4/learn/architecture#design-qa","Design Q&A",[128],"Why generate certificates automatically? Developer experience. For local development and testing, manual certificate management is friction. For production, WithTLSOptions() accepts external certificates. Why version-based topology sync instead of CRDTs? Simplicity. Version-based sync works for small clusters where a single source of truth exists. CRDTs add complexity for conflict resolution that isn't needed in most deployments. Why not use a service mesh like Istio? Aegis is embedded. No sidecars, no control plane, no Kubernetes dependency. It's a library, not infrastructure. Why gRPC? Protocol buffers provide typed contracts. gRPC provides streaming, deadlines, and interceptors. mTLS is built into gRPC's credential system.",{"id":205,"title":206,"titles":207,"content":208,"level":19},"/v0.0.4/learn/architecture#performance-characteristics","Performance Characteristics",[128],"OperationComplexityNotesGetServiceProvidersO(n × m)n nodes, m services per nodeTopology syncO(n)Full topology transferConnection pool lookupO(1)Map by addressRound-robin selectionO(1)Atomic counter For large meshes (100+ nodes), consider: Caching service provider queriesIncremental topology syncHierarchical topology (regional clusters)",{"id":210,"title":32,"titles":211,"content":212,"level":19},"/v0.0.4/learn/architecture#next-steps",[128],"Services Guide — Registering domain servicesAPI Reference — Function signatures html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}",{"id":214,"title":215,"titles":216,"content":217,"level":9},"/v0.0.4/guides/testing","Testing",[],"How to test code that uses aegis",{"id":219,"title":215,"titles":220,"content":221,"level":9},"/v0.0.4/guides/testing#testing",[],"This guide covers strategies for testing code that uses aegis.",{"id":223,"title":224,"titles":225,"content":226,"level":19},"/v0.0.4/guides/testing#test-helpers","Test Helpers",[215],"Aegis provides test helpers in the testing subpackage. Import with the testing build tag: //go:build testing\n\npackage mypackage_test\n\nimport (\n    \"testing\"\n    aegistesting \"github.com/zoobz-io/aegis/testing\"\n)",{"id":228,"title":229,"titles":230,"content":231,"level":150},"/v0.0.4/guides/testing#eventually","Eventually",[215,224],"Wait for a condition with timeout: func TestNodeStartup(t *testing.T) {\n    node := createTestNode(t)\n    node.StartServer()\n\n    aegistesting.Eventually(t, func() bool {\n        return node.IsHealthy()\n    }, 5*time.Second, 100*time.Millisecond)\n}",{"id":233,"title":234,"titles":235,"content":236,"level":150},"/v0.0.4/guides/testing#requirenoerror-requireerror","RequireNoError / RequireError",[215,224],"Assert on errors: aegistesting.RequireNoError(t, err)\naegistesting.RequireError(t, err)",{"id":238,"title":239,"titles":240,"content":241,"level":19},"/v0.0.4/guides/testing#creating-test-nodes","Creating Test Nodes",[215],"For unit tests, create nodes with temporary certificate directories: func createTestNode(t *testing.T, id string) *aegis.Node {\n    t.Helper()\n\n    certDir := t.TempDir()\n\n    node, err := aegis.NewNodeBuilder().\n        WithID(id).\n        WithName(\"Test Node \" + id).\n        WithAddress(\"localhost:0\"). // Random port\n        WithCertDir(certDir).\n        Build()\n\n    if err != nil {\n        t.Fatalf(\"failed to create node: %v\", err)\n    }\n\n    t.Cleanup(func() {\n        node.Shutdown()\n    })\n\n    return node\n} Using localhost:0 lets the OS assign an available port.",{"id":243,"title":244,"titles":245,"content":246,"level":19},"/v0.0.4/guides/testing#testing-service-providers","Testing Service Providers",[215],"To test a service implementation: func TestIdentityService(t *testing.T) {\n    // Create provider node\n    provider, err := aegis.NewNodeBuilder().\n        WithID(\"provider\").\n        WithName(\"Provider\").\n        WithAddress(\"localhost:0\").\n        WithServices(aegis.ServiceInfo{Name: \"identity\", Version: \"v1\"}).\n        WithServiceRegistration(func(s *grpc.Server) {\n            identity.RegisterIdentityServiceServer(s, &myIdentityServer{})\n        }).\n        WithCertDir(t.TempDir()).\n        Build()\n    require.NoError(t, err)\n\n    err = provider.StartServer()\n    require.NoError(t, err)\n    defer provider.Shutdown()\n\n    // Create consumer node with same CA\n    consumer, err := aegis.NewNodeBuilder().\n        WithID(\"consumer\").\n        WithName(\"Consumer\").\n        WithAddress(\"localhost:0\").\n        WithCertDir(t.TempDir()). // Different certs won't work!\n        Build()\n    require.NoError(t, err)\n\n    // ... test the service\n} Important: For nodes to communicate, they must trust the same CA. In tests, either: Share the certificate directoryUse a shared test CAPre-generate certificates",{"id":248,"title":249,"titles":250,"content":251,"level":19},"/v0.0.4/guides/testing#sharing-test-certificates","Sharing Test Certificates",[215],"Create a helper that generates a shared CA: func setupTestCA(t *testing.T) string {\n    t.Helper()\n    certDir := t.TempDir()\n\n    // Generate CA once\n    _, err := aegis.LoadOrGenerateTLS(\"test-ca\", certDir)\n    require.NoError(t, err)\n\n    return certDir\n}\n\nfunc createNodeWithCA(t *testing.T, id, certDir string) *aegis.Node {\n    t.Helper()\n\n    // Generate node cert using existing CA\n    _, err := aegis.LoadOrGenerateTLS(id, certDir)\n    require.NoError(t, err)\n\n    node, err := aegis.NewNodeBuilder().\n        WithID(id).\n        WithName(\"Node \" + id).\n        WithAddress(\"localhost:0\").\n        WithCertDir(certDir).\n        Build()\n    require.NoError(t, err)\n\n    return node\n}",{"id":253,"title":254,"titles":255,"content":256,"level":19},"/v0.0.4/guides/testing#testing-topology-sync","Testing Topology Sync",[215],"func TestTopologySync(t *testing.T) {\n    certDir := setupTestCA(t)\n\n    node1 := createNodeWithCA(t, \"node-1\", certDir)\n    node2 := createNodeWithCA(t, \"node-2\", certDir)\n\n    node1.StartServer()\n    node2.StartServer()\n    defer node1.Shutdown()\n    defer node2.Shutdown()\n\n    // Add node2 as peer of node1\n    err := node1.AddPeer(aegis.PeerInfo{\n        ID:      \"node-2\",\n        Type:    aegis.NodeTypeGeneric,\n        Address: node2.Address,\n    })\n    require.NoError(t, err)\n\n    // Sync topology\n    err = node1.SyncTopology(context.Background(), \"node-2\")\n    require.NoError(t, err)\n\n    // Verify both nodes appear in topology\n    nodes := node1.Topology.GetAllNodes()\n    assert.Len(t, nodes, 2)\n}",{"id":258,"title":259,"titles":260,"content":261,"level":19},"/v0.0.4/guides/testing#mocking-services","Mocking Services",[215],"For unit tests that don't need real gRPC: type mockIdentityClient struct {\n    validateResponse *identity.ValidateSessionResponse\n    validateError    error\n}\n\nfunc (m *mockIdentityClient) ValidateSession(ctx context.Context, req *identity.ValidateSessionRequest, opts ...grpc.CallOption) (*identity.ValidateSessionResponse, error) {\n    return m.validateResponse, m.validateError\n}\n\nfunc TestWithMockedIdentity(t *testing.T) {\n    mock := &mockIdentityClient{\n        validateResponse: &identity.ValidateSessionResponse{\n            Valid:  true,\n            UserId: \"user-123\",\n        },\n    }\n\n    // Use mock directly instead of ServiceClient\n    resp, err := mock.ValidateSession(ctx, &identity.ValidateSessionRequest{Token: \"test\"})\n    require.NoError(t, err)\n    assert.True(t, resp.Valid)\n}",{"id":263,"title":264,"titles":265,"content":266,"level":19},"/v0.0.4/guides/testing#integration-tests","Integration Tests",[215],"For end-to-end tests with real networking: //go:build integration\n\nfunc TestFullMeshCommunication(t *testing.T) {\n    if testing.Short() {\n        t.Skip(\"skipping integration test\")\n    }\n\n    // Create multiple nodes\n    // Start servers\n    // Add peers\n    // Sync topologies\n    // Make service calls\n    // Verify responses\n} Run with: go test -tags=integration ./...",{"id":268,"title":32,"titles":269,"content":270,"level":19},"/v0.0.4/guides/testing#next-steps",[215],"Troubleshooting — Common errors and solutionsServices Guide — Registering domain services html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}",{"id":272,"title":273,"titles":274,"content":275,"level":9},"/v0.0.4/guides/troubleshooting","Troubleshooting",[],"Common errors, edge cases, and debugging strategies",{"id":277,"title":273,"titles":278,"content":279,"level":9},"/v0.0.4/guides/troubleshooting#troubleshooting",[],"This guide covers common errors and how to resolve them.",{"id":281,"title":282,"titles":283,"content":144,"level":19},"/v0.0.4/guides/troubleshooting#connection-errors","Connection Errors",[273],{"id":285,"title":286,"titles":287,"content":288,"level":150},"/v0.0.4/guides/troubleshooting#certificate-signed-by-unknown-authority","\"certificate signed by unknown authority\"",[273,282],"Cause: Client doesn't trust the CA that signed the server's certificate. Solutions: Same certificate directory: Ensure both nodes use certificates from the same CA.// Both nodes should generate certs in the same directory\n// OR share the same CA files\nWithCertDir(\"./shared-certs\")\nCheck CA files: Verify ca-cert.pem exists and is the same on both sides.Custom CA: If using external certificates, ensure the CA is in the trust pool:opts := &aegis.TLSOptions{\n    Source:   aegis.CertSourceFile,\n    CAFile:   \"/path/to/ca.crt\",\n    CertFile: \"/path/to/node.crt\",\n    KeyFile:  \"/path/to/node.key\",\n}",{"id":290,"title":291,"titles":292,"content":293,"level":150},"/v0.0.4/guides/troubleshooting#connection-refused","\"connection refused\"",[273,282],"Cause: Server not running or wrong address. Solutions: Server started? Ensure node.StartServer() was called.Correct address? Check the peer's address matches the server's listening address.// Server\nWithAddress(\"localhost:8443\")\n\n// Client peer info must match\nnode.AddPeer(aegis.PeerInfo{\n    ID:      \"server-node\",\n    Type:    aegis.NodeTypeGeneric,\n    Address: \"localhost:8443\",\n})\nFirewall: Check if the port is blocked.",{"id":295,"title":296,"titles":297,"content":298,"level":150},"/v0.0.4/guides/troubleshooting#context-deadline-exceeded","\"context deadline exceeded\"",[273,282],"Cause: Connection or RPC timeout. Solutions: Network reachable? Verify nodes can reach each other.Increase timeout:ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\ndefer cancel()\nCheck server load: Server may be overwhelmed.",{"id":300,"title":301,"titles":302,"content":144,"level":19},"/v0.0.4/guides/troubleshooting#service-discovery-errors","Service Discovery Errors",[273],{"id":304,"title":305,"titles":306,"content":307,"level":150},"/v0.0.4/guides/troubleshooting#no-providers-available-for-service","\"no providers available for service\"",[273,301],"Cause: No nodes in topology provide the requested service. Solutions: Service declared? Provider must declare the service:WithServices(aegis.ServiceInfo{Name: \"identity\", Version: \"v1\"})\nTopology synced? Consumer's topology must include the provider:// Add provider as peer\nnode.AddPeer(aegis.PeerInfo{...})\n\n// Sync topology\nnode.SyncTopology(ctx, providerID)\n\n// Now query should work\nproviders := node.Topology.GetServiceProviders(\"identity\", \"v1\")\nVersion match? Service name AND version must match exactly.",{"id":309,"title":310,"titles":311,"content":312,"level":150},"/v0.0.4/guides/troubleshooting#node-has-no-tls-configuration","\"node has no TLS configuration\"",[273,301],"Cause: Attempting to connect without TLS setup. Solutions: Use NodeBuilder: Always use NewNodeBuilder() instead of NewNode().Specify cert source:WithCertDir(\"./certs\")\n// OR\nWithTLSOptions(&aegis.TLSOptions{...})",{"id":314,"title":315,"titles":316,"content":144,"level":19},"/v0.0.4/guides/troubleshooting#certificate-errors","Certificate Errors",[273],{"id":318,"title":319,"titles":320,"content":321,"level":150},"/v0.0.4/guides/troubleshooting#certificate-has-expired","\"certificate has expired\"",[273,315],"Cause: Node certificate past validity period. Solutions: Regenerate certificates: Delete old certs and restart node.rm certs/node-1-cert.pem certs/node-1-key.pem\nCheck CA expiry: CA certificates expire after 365 days by default.Disable expiry check (development only):opts := &aegis.TLSOptions{\n    AllowExpired: true,  // NOT for production\n}",{"id":323,"title":324,"titles":325,"content":326,"level":150},"/v0.0.4/guides/troubleshooting#certificate-does-not-contain-any-ip-sans","\"certificate does not contain any IP SANs\"",[273,315],"Cause: Certificate missing Subject Alternative Names for the connection address. Solutions: Use hostname: Connect via hostname, not IP, if cert has DNS SANs only.Regenerate with correct SANs: Delete cert and let aegis regenerate with proper SANs.",{"id":328,"title":329,"titles":330,"content":144,"level":19},"/v0.0.4/guides/troubleshooting#topology-issues","Topology Issues",[273],{"id":332,"title":333,"titles":334,"content":335,"level":150},"/v0.0.4/guides/troubleshooting#topology-not-syncing","Topology not syncing",[273,329],"Cause: Version comparison or connectivity issues. Debug steps: Check versions:log.Printf(\"Local version: %d\", node.Topology.GetVersion())\n// After sync\nlog.Printf(\"Local version: %d\", node.Topology.GetVersion())\nVerify peer connection:peer, exists := node.GetPeer(peerID)\nif !exists {\n    log.Println(\"Peer not found\")\n}\nif !peer.IsConnected() {\n    log.Println(\"Peer not connected\")\n}\nManual sync:err := node.SyncTopology(ctx, peerID)\nif err != nil {\n    log.Printf(\"Sync failed: %v\", err)\n}",{"id":337,"title":338,"titles":339,"content":340,"level":150},"/v0.0.4/guides/troubleshooting#stale-topology-data","Stale topology data",[273,329],"Cause: Topology not refreshed after changes. Solutions: Periodic sync:ticker := time.NewTicker(30 * time.Second)\nfor range ticker.C {\n    node.SyncTopologyWithAllPeers(ctx)\n}\nEvent-driven sync: Sync when peers connect/disconnect.",{"id":342,"title":343,"titles":344,"content":144,"level":19},"/v0.0.4/guides/troubleshooting#health-check-issues","Health Check Issues",[273],{"id":346,"title":347,"titles":348,"content":349,"level":150},"/v0.0.4/guides/troubleshooting#health-status-not-updating","Health status not updating",[273,343],"Cause: Health checker not being called. Solutions: Call CheckHealth explicitly:err := node.CheckHealth(ctx, checker)\nPeriodic health checks:go func() {\n    ticker := time.NewTicker(10 * time.Second)\n    for range ticker.C {\n        node.CheckHealth(ctx, checker)\n    }\n}()",{"id":351,"title":352,"titles":353,"content":144,"level":19},"/v0.0.4/guides/troubleshooting#debugging-strategies","Debugging Strategies",[273],{"id":355,"title":356,"titles":357,"content":358,"level":150},"/v0.0.4/guides/troubleshooting#enable-grpc-logging","Enable gRPC logging",[273,352],"import \"google.golang.org/grpc/grpclog\"\n\ngrpclog.SetLoggerV2(grpclog.NewLoggerV2(os.Stdout, os.Stdout, os.Stderr))",{"id":360,"title":361,"titles":362,"content":363,"level":150},"/v0.0.4/guides/troubleshooting#inspect-certificates","Inspect certificates",[273,352],"# View certificate details\nopenssl x509 -in certs/node-1-cert.pem -text -noout\n\n# Verify certificate chain\nopenssl verify -CAfile certs/ca-cert.pem certs/node-1-cert.pem",{"id":365,"title":366,"titles":367,"content":368,"level":150},"/v0.0.4/guides/troubleshooting#check-connection-state","Check connection state",[273,352],"peer, _ := node.GetPeer(peerID)\nstate := peer.Conn.GetState()\nlog.Printf(\"Connection state: %v\", state)\n// IDLE, CONNECTING, READY, TRANSIENT_FAILURE, SHUTDOWN",{"id":370,"title":32,"titles":371,"content":372,"level":19},"/v0.0.4/guides/troubleshooting#next-steps",[273],"Services Guide — Registering domain servicesCertificates Guide — Certificate management details html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}",{"id":374,"title":375,"titles":376,"content":377,"level":9},"/v0.0.4/guides/services","Services",[],"Defining, registering, and consuming domain services",{"id":379,"title":375,"titles":380,"content":381,"level":9},"/v0.0.4/guides/services#services",[],"This guide covers how to define, register, and consume domain services in the aegis mesh.",{"id":383,"title":384,"titles":385,"content":386,"level":19},"/v0.0.4/guides/services#defining-services","Defining Services",[375],"Services are defined using Protocol Buffers. Aegis hosts shared proto files in proto/: aegis/\n└── proto/\n    └── identity/\n        ├── identity.proto\n        ├── identity.pb.go\n        └── identity_grpc.pb.go",{"id":388,"title":389,"titles":390,"content":391,"level":150},"/v0.0.4/guides/services#example-proto","Example Proto",[375,384],"syntax = \"proto3\";\n\npackage aegis.identity.v1;\n\noption go_package = \"github.com/zoobz-io/aegis/proto/identity\";\n\nservice IdentityService {\n  rpc ValidateSession(ValidateSessionRequest) returns (ValidateSessionResponse);\n  rpc GetUser(GetUserRequest) returns (GetUserResponse);\n}\n\nmessage ValidateSessionRequest {\n  string token = 1;\n}\n\nmessage ValidateSessionResponse {\n  bool valid = 1;\n  string user_id = 2;\n  int64 expires_at = 3;\n}",{"id":393,"title":394,"titles":395,"content":396,"level":150},"/v0.0.4/guides/services#generating-code","Generating Code",[375,384],"protoc --go_out=. --go_opt=paths=source_relative \\\n       --go-grpc_out=. --go-grpc_opt=paths=source_relative \\\n       proto/identity/identity.proto Or use the Makefile: make proto",{"id":398,"title":399,"titles":400,"content":144,"level":19},"/v0.0.4/guides/services#registering-services","Registering Services",[375],{"id":402,"title":403,"titles":404,"content":405,"level":150},"/v0.0.4/guides/services#implementing-the-server","Implementing the Server",[375,399],"package main\n\nimport (\n    \"context\"\n\n    \"github.com/zoobz-io/aegis\"\n    identity \"github.com/zoobz-io/aegis/proto/identity\"\n)\n\ntype identityServer struct {\n    identity.UnimplementedIdentityServiceServer\n    // Add your dependencies\n    sessions SessionStore\n    users    UserStore\n}\n\nfunc (s *identityServer) ValidateSession(ctx context.Context, req *identity.ValidateSessionRequest) (*identity.ValidateSessionResponse, error) {\n    session, err := s.sessions.Get(ctx, req.Token)\n    if err != nil {\n        return &identity.ValidateSessionResponse{Valid: false}, nil\n    }\n\n    return &identity.ValidateSessionResponse{\n        Valid:     true,\n        UserId:    session.UserID,\n        ExpiresAt: session.ExpiresAt.Unix(),\n    }, nil\n}",{"id":407,"title":408,"titles":409,"content":410,"level":150},"/v0.0.4/guides/services#registering-with-node","Registering with Node",[375,399],"Use WithServiceRegistration to register your service: node, err := aegis.NewNodeBuilder().\n    WithID(\"morpheus-1\").\n    WithName(\"Morpheus\").\n    WithAddress(\"localhost:8443\").\n    WithServices(aegis.ServiceInfo{Name: \"identity\", Version: \"v1\"}).\n    WithServiceRegistration(func(s *grpc.Server) {\n        identity.RegisterIdentityServiceServer(s, &identityServer{\n            sessions: mySessionStore,\n            users:    myUserStore,\n        })\n    }).\n    WithCertDir(\"./certs\").\n    Build() The WithServices declaration advertises the service in topology. The WithServiceRegistration callback registers the gRPC handler.",{"id":412,"title":413,"titles":414,"content":415,"level":150},"/v0.0.4/guides/services#multiple-services","Multiple Services",[375,399],"Register multiple services by chaining: WithServices(\n    aegis.ServiceInfo{Name: \"identity\", Version: \"v1\"},\n    aegis.ServiceInfo{Name: \"audit\", Version: \"v1\"},\n).\nWithServiceRegistration(func(s *grpc.Server) {\n    identity.RegisterIdentityServiceServer(s, &identityServer{})\n    audit.RegisterAuditServiceServer(s, &auditServer{})\n})",{"id":417,"title":418,"titles":419,"content":144,"level":19},"/v0.0.4/guides/services#consuming-services","Consuming Services",[375],{"id":421,"title":422,"titles":423,"content":424,"level":150},"/v0.0.4/guides/services#creating-a-client","Creating a Client",[375,418],"pool := aegis.NewServiceClientPool(node)\ndefer pool.Close()\n\nclient := aegis.NewServiceClient(pool, \"identity\", \"v1\", identity.NewIdentityServiceClient)",{"id":426,"title":427,"titles":428,"content":429,"level":150},"/v0.0.4/guides/services#making-calls","Making Calls",[375,418],"// Get a client (round-robin across providers)\nidentityClient, err := client.Get(ctx)\nif err != nil {\n    return err // No providers available\n}\n\nresp, err := identityClient.ValidateSession(ctx, &identity.ValidateSessionRequest{\n    Token: userToken,\n})\nif err != nil {\n    return err // RPC failed\n}\n\nif resp.Valid {\n    log.Printf(\"User: %s\", resp.UserId)\n}",{"id":431,"title":432,"titles":433,"content":434,"level":150},"/v0.0.4/guides/services#error-handling","Error Handling",[375,418],"identityClient, err := client.Get(ctx)\nif errors.Is(err, aegis.ErrNoProviders) {\n    // No nodes provide this service\n    return fallbackBehavior()\n}\nif errors.Is(err, aegis.ErrNoTLSConfig) {\n    // Node not configured with TLS\n    return configurationError()\n}",{"id":436,"title":437,"titles":438,"content":439,"level":19},"/v0.0.4/guides/services#caller-authorization","Caller Authorization",[375],"Services can authorize callers using mTLS identity: func (s *identityServer) ValidateSession(ctx context.Context, req *identity.ValidateSessionRequest) (*identity.ValidateSessionResponse, error) {\n    caller, err := aegis.CallerFromContext(ctx)\n    if err != nil {\n        return nil, status.Error(codes.Unauthenticated, \"no caller identity\")\n    }\n\n    // Check if caller is allowed\n    if !s.allowedNodes[caller.NodeID] {\n        return nil, status.Error(codes.PermissionDenied, \"node not authorized\")\n    }\n\n    // Proceed with request\n    // ...\n}",{"id":441,"title":442,"titles":443,"content":444,"level":150},"/v0.0.4/guides/services#allowlist-pattern","Allowlist Pattern",[375,437],"type identityServer struct {\n    identity.UnimplementedIdentityServiceServer\n    allowedNodes map[string]bool\n}\n\nfunc NewIdentityServer(allowedNodes []string) *identityServer {\n    allowed := make(map[string]bool)\n    for _, id := range allowedNodes {\n        allowed[id] = true\n    }\n    return &identityServer{allowedNodes: allowed}\n}",{"id":446,"title":447,"titles":448,"content":449,"level":150},"/v0.0.4/guides/services#node-type-authorization","Node Type Authorization",[375,437],"// In the calling node\nWithType(aegis.NodeType(\"api-gateway\"))\n\n// In the service\ncaller, _ := aegis.CallerFromContext(ctx)\ncert := caller.Certificate\n// Extract node type from certificate or lookup from topology",{"id":451,"title":452,"titles":453,"content":144,"level":19},"/v0.0.4/guides/services#service-versioning","Service Versioning",[375],{"id":455,"title":456,"titles":457,"content":458,"level":150},"/v0.0.4/guides/services#multiple-versions","Multiple Versions",[375,452],"Run multiple versions simultaneously: WithServices(\n    aegis.ServiceInfo{Name: \"identity\", Version: \"v1\"},\n    aegis.ServiceInfo{Name: \"identity\", Version: \"v2\"},\n)",{"id":460,"title":461,"titles":462,"content":463,"level":150},"/v0.0.4/guides/services#version-selection","Version Selection",[375,452],"Clients explicitly request a version: // v1 client\nclientV1 := aegis.NewServiceClient(pool, \"identity\", \"v1\", identityv1.NewIdentityServiceClient)\n\n// v2 client\nclientV2 := aegis.NewServiceClient(pool, \"identity\", \"v2\", identityv2.NewIdentityServiceClient)",{"id":465,"title":466,"titles":467,"content":468,"level":150},"/v0.0.4/guides/services#migration-strategy","Migration Strategy",[375,452],"Deploy providers with both v1 and v2Update consumers to use v2Remove v1 from providersRemove v1 proto definitions",{"id":470,"title":32,"titles":471,"content":472,"level":19},"/v0.0.4/guides/services#next-steps",[375],"Certificates Guide — Certificate managementAPI Reference — Function signatures html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}",{"id":474,"title":475,"titles":476,"content":477,"level":9},"/v0.0.4/guides/certificates","Certificates",[],"Certificate management, sources, and production configuration",{"id":479,"title":475,"titles":480,"content":481,"level":9},"/v0.0.4/guides/certificates#certificates",[],"Aegis requires mTLS for all node-to-node communication. This guide explains certificate management for development and production.",{"id":483,"title":484,"titles":485,"content":486,"level":19},"/v0.0.4/guides/certificates#development-mode","Development Mode",[475],"For local development, aegis generates certificates automatically: node, _ := aegis.NewNodeBuilder().\n    WithID(\"dev-node\").\n    WithName(\"Development Node\").\n    WithAddress(\"localhost:8443\").\n    WithCertDir(\"./certs\").\n    Build() On first run: Generates CA certificate (ca-cert.pem, ca-key.pem)Generates node certificate (dev-node-cert.pem, dev-node-key.pem) certs/\n├── ca-cert.pem         # CA certificate (365-day validity)\n├── ca-key.pem          # CA private key\n├── dev-node-cert.pem   # Node certificate (90-day validity)\n└── dev-node-key.pem    # Node private key This is for development only. For production, use external certificates.",{"id":488,"title":489,"titles":490,"content":144,"level":19},"/v0.0.4/guides/certificates#production-configuration","Production Configuration",[475],{"id":492,"title":493,"titles":494,"content":495,"level":150},"/v0.0.4/guides/certificates#certificate-sources","Certificate Sources",[475,489],"Aegis supports three certificate sources: SourceUse CaseCertSourceFileFiles on diskCertSourceEnvEnvironment variablesCertSourceVaultHashiCorp Vault (future)",{"id":497,"title":498,"titles":499,"content":500,"level":150},"/v0.0.4/guides/certificates#file-based-certificates","File-Based Certificates",[475,489],"opts := &aegis.TLSOptions{\n    Source:   aegis.CertSourceFile,\n    CertFile: \"/etc/aegis/certs/node.crt\",\n    KeyFile:  \"/etc/aegis/certs/node.key\",\n    CAFile:   \"/etc/aegis/certs/ca.crt\",\n}\n\nnode, _ := aegis.NewNodeBuilder().\n    WithID(\"prod-node\").\n    WithName(\"Production Node\").\n    WithAddress(\"node.example.com:8443\").\n    WithTLSOptions(opts).\n    Build() File permissions: Ensure key files are readable only by the node process (mode 0600).",{"id":502,"title":503,"titles":504,"content":505,"level":150},"/v0.0.4/guides/certificates#environment-variables","Environment Variables",[475,489],"For containers and orchestrated deployments: opts := &aegis.TLSOptions{\n    Source:    aegis.CertSourceEnv,\n    CertEnvVar: \"AEGIS_TLS_CERT\",\n    KeyEnvVar:  \"AEGIS_TLS_KEY\",\n    CAEnvVar:   \"AEGIS_TLS_CA\",\n} Set environment variables with PEM-encoded certificates: export AEGIS_TLS_CERT=\"-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAKl...\n-----END CERTIFICATE-----\"\n\nexport AEGIS_TLS_KEY=\"-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAvLYcyu8f3...\n-----END RSA PRIVATE KEY-----\"\n\nexport AEGIS_TLS_CA=\"-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAKl1...\n-----END CERTIFICATE-----\"",{"id":507,"title":508,"titles":509,"content":510,"level":150},"/v0.0.4/guides/certificates#kubernetes-secrets","Kubernetes Secrets",[475,489],"apiVersion: v1\nkind: Secret\nmetadata:\n  name: aegis-tls\ntype: Opaque\ndata:\n  cert: LS0tLS1CRUdJTi...  # base64 encoded\n  key: LS0tLS1CRUdJTi...\n  ca: LS0tLS1CRUdJTi... # Pod spec\nenv:\n  - name: AEGIS_TLS_CERT\n    valueFrom:\n      secretKeyRef:\n        name: aegis-tls\n        key: cert\n  - name: AEGIS_TLS_KEY\n    valueFrom:\n      secretKeyRef:\n        name: aegis-tls\n        key: key\n  - name: AEGIS_TLS_CA\n    valueFrom:\n      secretKeyRef:\n        name: aegis-tls\n        key: ca",{"id":512,"title":513,"titles":514,"content":144,"level":19},"/v0.0.4/guides/certificates#certificate-validation","Certificate Validation",[475],{"id":516,"title":517,"titles":518,"content":519,"level":150},"/v0.0.4/guides/certificates#chain-verification","Chain Verification",[475,513],"Enable full chain verification: opts := &aegis.TLSOptions{\n    Source:      aegis.CertSourceFile,\n    CertFile:    \"/path/to/node.crt\",\n    KeyFile:     \"/path/to/node.key\",\n    CAFile:      \"/path/to/ca.crt\",\n    VerifyChain: true,\n}",{"id":521,"title":522,"titles":523,"content":524,"level":150},"/v0.0.4/guides/certificates#required-sans","Required SANs",[475,513],"Enforce Subject Alternative Name validation: opts := &aegis.TLSOptions{\n    Source:       aegis.CertSourceFile,\n    CertFile:     \"/path/to/node.crt\",\n    KeyFile:      \"/path/to/node.key\",\n    CAFile:       \"/path/to/ca.crt\",\n    RequiredSANs: []string{\"*.aegis.internal\", \"node.example.com\"},\n}",{"id":526,"title":527,"titles":528,"content":529,"level":150},"/v0.0.4/guides/certificates#expiry-handling","Expiry Handling",[475,513],"By default, expired certificates are rejected. For testing or migration, this can be disabled: opts := &aegis.TLSOptions{\n    AllowExpired: true,  // NOT FOR PRODUCTION\n}",{"id":531,"title":532,"titles":533,"content":144,"level":19},"/v0.0.4/guides/certificates#generating-certificates","Generating Certificates",[475],{"id":535,"title":536,"titles":537,"content":538,"level":150},"/v0.0.4/guides/certificates#using-openssl","Using OpenSSL",[475,532],"Generate a CA: # Generate CA key\nopenssl genrsa -out ca.key 4096\n\n# Generate CA certificate\nopenssl req -new -x509 -days 365 -key ca.key -out ca.crt \\\n    -subj \"/CN=Aegis CA/O=My Organization\" Generate a node certificate: # Generate node key\nopenssl genrsa -out node.key 2048\n\n# Generate CSR\nopenssl req -new -key node.key -out node.csr \\\n    -subj \"/CN=node-1/O=Aegis Network\"\n\n# Create SAN config\ncat > san.cnf \u003C\u003C EOF\n[req]\ndistinguished_name = req_distinguished_name\nreq_extensions = v3_req\n\n[req_distinguished_name]\nCN = node-1\n\n[v3_req]\nsubjectAltName = @alt_names\n\n[alt_names]\nDNS.1 = node-1\nDNS.2 = localhost\nIP.1 = 127.0.0.1\nEOF\n\n# Sign with CA\nopenssl x509 -req -days 90 -in node.csr -CA ca.crt -CAkey ca.key \\\n    -CAcreateserial -out node.crt -extfile san.cnf -extensions v3_req",{"id":540,"title":541,"titles":542,"content":543,"level":150},"/v0.0.4/guides/certificates#using-cfssl","Using cfssl",[475,532],"# Generate CA\ncfssl gencert -initca ca-csr.json | cfssljson -bare ca\n\n# Generate node certificate\ncfssl gencert -ca=ca.pem -ca-key=ca-key.pem \\\n    -config=cfssl.json -profile=client-server \\\n    node-csr.json | cfssljson -bare node",{"id":545,"title":546,"titles":547,"content":548,"level":19},"/v0.0.4/guides/certificates#security-best-practices","Security Best Practices",[475],"Use external CA — Never generate CA certificates on application nodes in productionRotate certificates — Node certificates should be rotated before expirySecure key storage — Use hardware security modules (HSM) or key management servicesMonitor expiration — Alert on certificates approaching expiryAudit failures — Log all certificate validation failuresMinimum key size — Use 2048-bit RSA or 256-bit ECDSA minimum",{"id":550,"title":551,"titles":552,"content":144,"level":19},"/v0.0.4/guides/certificates#debugging","Debugging",[475],{"id":554,"title":555,"titles":556,"content":557,"level":150},"/v0.0.4/guides/certificates#view-certificate-details","View Certificate Details",[475,551],"openssl x509 -in node.crt -text -noout",{"id":559,"title":560,"titles":561,"content":562,"level":150},"/v0.0.4/guides/certificates#verify-chain","Verify Chain",[475,551],"openssl verify -CAfile ca.crt node.crt",{"id":564,"title":565,"titles":566,"content":567,"level":150},"/v0.0.4/guides/certificates#test-mtls-connection","Test mTLS Connection",[475,551],"openssl s_client -connect localhost:8443 \\\n    -cert client.crt \\\n    -key client.key \\\n    -CAfile ca.crt",{"id":569,"title":32,"titles":570,"content":571,"level":19},"/v0.0.4/guides/certificates#next-steps",[475],"Troubleshooting — Common certificate errorsArchitecture — How mTLS works internally html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}",{"id":573,"title":574,"titles":575,"content":576,"level":9},"/v0.0.4/reference/api","API Reference",[],"Function signatures and usage",{"id":578,"title":574,"titles":579,"content":580,"level":9},"/v0.0.4/reference/api#api-reference",[],"This document covers the public API of aegis.",{"id":582,"title":583,"titles":584,"content":144,"level":19},"/v0.0.4/reference/api#node-builder","Node Builder",[574],{"id":586,"title":587,"titles":588,"content":589,"level":150},"/v0.0.4/reference/api#newnodebuilder","NewNodeBuilder",[574,583],"func NewNodeBuilder() *NodeBuilder Creates a new node builder with defaults: Type: NodeTypeGenericCertDir: \"./certs\" Example: builder := aegis.NewNodeBuilder()",{"id":591,"title":592,"titles":593,"content":594,"level":150},"/v0.0.4/reference/api#nodebuilderwithid","NodeBuilder.WithID",[574,583],"func (nb *NodeBuilder) WithID(id string) *NodeBuilder Sets the node ID. Required.",{"id":596,"title":597,"titles":598,"content":599,"level":150},"/v0.0.4/reference/api#nodebuilderwithname","NodeBuilder.WithName",[574,583],"func (nb *NodeBuilder) WithName(name string) *NodeBuilder Sets the node name. Required.",{"id":601,"title":602,"titles":603,"content":604,"level":150},"/v0.0.4/reference/api#nodebuilderwithtype","NodeBuilder.WithType",[574,583],"func (nb *NodeBuilder) WithType(nodeType NodeType) *NodeBuilder Sets the node type. Optional, defaults to NodeTypeGeneric.",{"id":606,"title":607,"titles":608,"content":609,"level":150},"/v0.0.4/reference/api#nodebuilderwithaddress","NodeBuilder.WithAddress",[574,583],"func (nb *NodeBuilder) WithAddress(address string) *NodeBuilder Sets the node address (host:port). Required.",{"id":611,"title":612,"titles":613,"content":614,"level":150},"/v0.0.4/reference/api#nodebuilderwithservices","NodeBuilder.WithServices",[574,583],"func (nb *NodeBuilder) WithServices(services ...ServiceInfo) *NodeBuilder Declares services this node provides. Optional.",{"id":616,"title":617,"titles":618,"content":619,"level":150},"/v0.0.4/reference/api#nodebuilderwithserviceregistration","NodeBuilder.WithServiceRegistration",[574,583],"func (nb *NodeBuilder) WithServiceRegistration(r ServiceRegistrar) *NodeBuilder Adds a callback to register gRPC services. Called when server starts. Example: WithServiceRegistration(func(s *grpc.Server) {\n    identity.RegisterIdentityServiceServer(s, &myServer{})\n})",{"id":621,"title":622,"titles":623,"content":624,"level":150},"/v0.0.4/reference/api#nodebuilderwithcertdir","NodeBuilder.WithCertDir",[574,583],"func (nb *NodeBuilder) WithCertDir(certDir string) *NodeBuilder Sets the certificate directory. Certificates are generated if missing.",{"id":626,"title":627,"titles":628,"content":629,"level":150},"/v0.0.4/reference/api#nodebuilderwithtlsoptions","NodeBuilder.WithTLSOptions",[574,583],"func (nb *NodeBuilder) WithTLSOptions(opts *TLSOptions) *NodeBuilder Sets custom TLS options. Overrides WithCertDir.",{"id":631,"title":632,"titles":633,"content":634,"level":150},"/v0.0.4/reference/api#nodebuilderbuild","NodeBuilder.Build",[574,583],"func (nb *NodeBuilder) Build() (*Node, error) Creates the node. Returns error if required fields missing or TLS setup fails.",{"id":636,"title":89,"titles":637,"content":144,"level":19},"/v0.0.4/reference/api#node",[574],{"id":639,"title":640,"titles":641,"content":642,"level":150},"/v0.0.4/reference/api#nodestartserver","Node.StartServer",[574,89],"func (n *Node) StartServer() error Starts the gRPC server. Returns error if TLS not configured or bind fails.",{"id":644,"title":645,"titles":646,"content":647,"level":150},"/v0.0.4/reference/api#nodeshutdown","Node.Shutdown",[574,89],"func (n *Node) Shutdown() error Gracefully shuts down server and closes peer connections.",{"id":649,"title":650,"titles":651,"content":652,"level":150},"/v0.0.4/reference/api#nodeaddpeer","Node.AddPeer",[574,89],"func (n *Node) AddPeer(info PeerInfo) error Adds a peer connection. Connection established on first use.",{"id":654,"title":655,"titles":656,"content":657,"level":150},"/v0.0.4/reference/api#noderemovepeer","Node.RemovePeer",[574,89],"func (n *Node) RemovePeer(peerID string) error Removes a peer and closes its connection.",{"id":659,"title":660,"titles":661,"content":662,"level":150},"/v0.0.4/reference/api#nodegetpeer","Node.GetPeer",[574,89],"func (n *Node) GetPeer(peerID string) (*Peer, bool) Returns a peer by ID.",{"id":664,"title":665,"titles":666,"content":667,"level":150},"/v0.0.4/reference/api#nodegetallpeers","Node.GetAllPeers",[574,89],"func (n *Node) GetAllPeers() []*Peer Returns all connected peers.",{"id":669,"title":670,"titles":671,"content":672,"level":150},"/v0.0.4/reference/api#nodepingpeer","Node.PingPeer",[574,89],"func (n *Node) PingPeer(ctx context.Context, peerID string) (*PingResponse, error) Pings a peer and returns response with latency.",{"id":674,"title":675,"titles":676,"content":677,"level":150},"/v0.0.4/reference/api#nodegetpeerhealth","Node.GetPeerHealth",[574,89],"func (n *Node) GetPeerHealth(ctx context.Context, peerID string) (*HealthResponse, error) Gets health status from a peer.",{"id":679,"title":680,"titles":681,"content":682,"level":150},"/v0.0.4/reference/api#nodesynctopology","Node.SyncTopology",[574,89],"func (n *Node) SyncTopology(ctx context.Context, peerID string) error Synchronizes topology with a specific peer.",{"id":684,"title":685,"titles":686,"content":687,"level":150},"/v0.0.4/reference/api#nodesynctopologywithallpeers","Node.SyncTopologyWithAllPeers",[574,89],"func (n *Node) SyncTopologyWithAllPeers(ctx context.Context) error Synchronizes topology with all connected peers.",{"id":689,"title":690,"titles":691,"content":692,"level":150},"/v0.0.4/reference/api#nodesethealth","Node.SetHealth",[574,89],"func (n *Node) SetHealth(status HealthStatus, message string, err error) Sets the node's health status.",{"id":694,"title":695,"titles":696,"content":697,"level":150},"/v0.0.4/reference/api#nodecheckhealth","Node.CheckHealth",[574,89],"func (n *Node) CheckHealth(ctx context.Context, checker HealthChecker) error Runs a health checker and updates status.",{"id":699,"title":700,"titles":701,"content":702,"level":150},"/v0.0.4/reference/api#nodeishealthy","Node.IsHealthy",[574,89],"func (n *Node) IsHealthy() bool Returns true if node status is healthy.",{"id":704,"title":99,"titles":705,"content":144,"level":19},"/v0.0.4/reference/api#topology",[574],{"id":707,"title":708,"titles":709,"content":710,"level":150},"/v0.0.4/reference/api#topologyaddnode","Topology.AddNode",[574,99],"func (t *Topology) AddNode(info NodeInfo) error Adds a node to topology. Error if node already exists.",{"id":712,"title":713,"titles":714,"content":715,"level":150},"/v0.0.4/reference/api#topologyremovenode","Topology.RemoveNode",[574,99],"func (t *Topology) RemoveNode(nodeID string) error Removes a node from topology. Error if not found.",{"id":717,"title":718,"titles":719,"content":720,"level":150},"/v0.0.4/reference/api#topologygetnode","Topology.GetNode",[574,99],"func (t *Topology) GetNode(nodeID string) (NodeInfo, bool) Returns a node by ID.",{"id":722,"title":723,"titles":724,"content":725,"level":150},"/v0.0.4/reference/api#topologygetallnodes","Topology.GetAllNodes",[574,99],"func (t *Topology) GetAllNodes() []NodeInfo Returns all nodes in topology.",{"id":727,"title":728,"titles":729,"content":730,"level":150},"/v0.0.4/reference/api#topologygetserviceproviders","Topology.GetServiceProviders",[574,99],"func (t *Topology) GetServiceProviders(name, version string) []NodeInfo Returns nodes providing the specified service and version.",{"id":732,"title":733,"titles":734,"content":735,"level":150},"/v0.0.4/reference/api#topologygetnodesbyservice","Topology.GetNodesByService",[574,99],"func (t *Topology) GetNodesByService(name string) []NodeInfo Returns nodes providing any version of the specified service.",{"id":737,"title":738,"titles":739,"content":740,"level":150},"/v0.0.4/reference/api#topologygetversion","Topology.GetVersion",[574,99],"func (t *Topology) GetVersion() int64 Returns the topology version number.",{"id":742,"title":109,"titles":743,"content":144,"level":19},"/v0.0.4/reference/api#serviceclientpool",[574],{"id":745,"title":746,"titles":747,"content":748,"level":150},"/v0.0.4/reference/api#newserviceclientpool","NewServiceClientPool",[574,109],"func NewServiceClientPool(node *Node) *ServiceClientPool Creates a connection pool for service clients.",{"id":750,"title":751,"titles":752,"content":753,"level":150},"/v0.0.4/reference/api#serviceclientpoolgetconn","ServiceClientPool.GetConn",[574,109],"func (p *ServiceClientPool) GetConn(ctx context.Context, name, version string) (*grpc.ClientConn, error) Returns a connection to a service provider. Uses round-robin across providers. Errors: ErrNoProviders — No nodes provide this serviceErrNoTLSConfig — Node has no TLS configuration",{"id":755,"title":756,"titles":757,"content":758,"level":150},"/v0.0.4/reference/api#serviceclientpoolclose","ServiceClientPool.Close",[574,109],"func (p *ServiceClientPool) Close() error Closes all connections in the pool.",{"id":760,"title":761,"titles":762,"content":144,"level":19},"/v0.0.4/reference/api#serviceclient","ServiceClient",[574],{"id":764,"title":765,"titles":766,"content":767,"level":150},"/v0.0.4/reference/api#newserviceclient","NewServiceClient",[574,761],"func NewServiceClient[T any](pool *ServiceClientPool, name, version string, newClient func(grpc.ClientConnInterface) T) *ServiceClient[T] Creates a typed service client. Example: client := aegis.NewServiceClient(pool, \"identity\", \"v1\", identity.NewIdentityServiceClient)",{"id":769,"title":770,"titles":771,"content":772,"level":150},"/v0.0.4/reference/api#serviceclientget","ServiceClient.Get",[574,761],"func (sc *ServiceClient[T]) Get(ctx context.Context) (T, error) Returns a client connected to a provider.",{"id":774,"title":775,"titles":776,"content":144,"level":19},"/v0.0.4/reference/api#context","Context",[574],{"id":778,"title":779,"titles":780,"content":781,"level":150},"/v0.0.4/reference/api#callerfromcontext","CallerFromContext",[574,775],"func CallerFromContext(ctx context.Context) (*Caller, error) Extracts caller identity from gRPC context. Errors: ErrNoPeerInfo — No peer info in contextErrNoTLSInfo — Peer has no TLS infoErrNoCertificate — No client certificate",{"id":783,"title":784,"titles":785,"content":786,"level":150},"/v0.0.4/reference/api#mustcallerfromcontext","MustCallerFromContext",[574,775],"func MustCallerFromContext(ctx context.Context) *Caller Extracts caller identity, panics on error. Use only when mTLS is guaranteed.",{"id":788,"title":789,"titles":790,"content":144,"level":19},"/v0.0.4/reference/api#health","Health",[574],{"id":792,"title":793,"titles":794,"content":795,"level":150},"/v0.0.4/reference/api#newhealthinfo","NewHealthInfo",[574,789],"func NewHealthInfo() *HealthInfo Creates health info with unknown status.",{"id":797,"title":798,"titles":799,"content":800,"level":150},"/v0.0.4/reference/api#newfunctionhealthchecker","NewFunctionHealthChecker",[574,789],"func NewFunctionHealthChecker(name string, fn func(context.Context) error) *FunctionHealthChecker Creates a health checker from a function.",{"id":802,"title":803,"titles":804,"content":144,"level":19},"/v0.0.4/reference/api#tls","TLS",[574],{"id":806,"title":807,"titles":808,"content":809,"level":150},"/v0.0.4/reference/api#loadorgeneratetls","LoadOrGenerateTLS",[574,803],"func LoadOrGenerateTLS(nodeID, certDir string) (*TLSConfig, error) Loads certificates from directory, generating if missing.",{"id":811,"title":812,"titles":813,"content":814,"level":150},"/v0.0.4/reference/api#loadtlsconfig","LoadTLSConfig",[574,803],"func LoadTLSConfig(opts *TLSOptions) (*TLSConfig, error) Loads TLS configuration from options.",{"id":816,"title":32,"titles":817,"content":818,"level":19},"/v0.0.4/reference/api#next-steps",[574],"Types Reference — Type definitionsConcepts — Mental models html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}",{"id":820,"title":821,"titles":822,"content":823,"level":9},"/v0.0.4/reference/types","Types Reference",[],"Type definitions and field descriptions",{"id":825,"title":821,"titles":826,"content":827,"level":9},"/v0.0.4/reference/types#types-reference",[],"This document describes the core types in aegis.",{"id":829,"title":89,"titles":830,"content":831,"level":19},"/v0.0.4/reference/types#node",[821],"type Node struct {\n    ID          string\n    Name        string\n    Type        NodeType\n    Address     string\n    Services    []ServiceInfo\n    Health      *HealthInfo\n    PeerManager *PeerManager\n    MeshServer  *MeshServer\n    Topology    *Topology\n    TLSConfig   *TLSConfig\n} FieldTypeDescriptionIDstringUnique identifier, used in certificatesNamestringHuman-readable labelTypeNodeTypeClassification (e.g., gateway, worker)AddressstringHost:port for connectionsServices[]ServiceInfoServices this node providesHealth*HealthInfoCurrent health statusPeerManager*PeerManagerManages outgoing connectionsMeshServer*MeshServergRPC server for incoming connectionsTopology*TopologyView of mesh membershipTLSConfig*TLSConfigCertificates and TLS settings",{"id":833,"title":834,"titles":835,"content":836,"level":19},"/v0.0.4/reference/types#nodetype","NodeType",[821],"type NodeType string\n\nconst (\n    NodeTypeGeneric NodeType = \"generic\"\n) Node classification. Applications can define custom types.",{"id":838,"title":839,"titles":840,"content":841,"level":19},"/v0.0.4/reference/types#nodeinfo","NodeInfo",[821],"type NodeInfo struct {\n    ID        string\n    Name      string\n    Type      NodeType\n    Address   string\n    Services  []ServiceInfo\n    JoinedAt  time.Time\n    UpdatedAt time.Time\n} FieldTypeDescriptionIDstringNode identifierNamestringNode nameTypeNodeTypeNode typeAddressstringNode addressServices[]ServiceInfoServices the node providesJoinedAttime.TimeWhen node joined topologyUpdatedAttime.TimeLast update timestamp",{"id":843,"title":844,"titles":845,"content":846,"level":19},"/v0.0.4/reference/types#serviceinfo","ServiceInfo",[821],"type ServiceInfo struct {\n    Name    string\n    Version string\n} FieldTypeDescriptionNamestringService name (e.g., \"identity\")VersionstringService version (e.g., \"v1\")",{"id":848,"title":849,"titles":850,"content":851,"level":19},"/v0.0.4/reference/types#peerinfo","PeerInfo",[821],"type PeerInfo struct {\n    ID      string\n    Type    NodeType\n    Address string\n} FieldTypeDescriptionIDstringPeer node IDTypeNodeTypePeer node typeAddressstringPeer address for connection",{"id":853,"title":94,"titles":854,"content":855,"level":19},"/v0.0.4/reference/types#peer",[821],"type Peer struct {\n    Info   PeerInfo\n    Client MeshServiceClient\n    Conn   *grpc.ClientConn\n} FieldTypeDescriptionInfoPeerInfoPeer metadataClientMeshServiceClientgRPC client for mesh operationsConn*grpc.ClientConnUnderlying connection",{"id":857,"title":99,"titles":858,"content":859,"level":19},"/v0.0.4/reference/types#topology",[821],"type Topology struct {\n    Nodes     map[string]NodeInfo\n    Version   int64\n    UpdatedAt time.Time\n} FieldTypeDescriptionNodesmap[string]NodeInfoNode ID to info mappingVersionint64Monotonically increasing versionUpdatedAttime.TimeLast modification time",{"id":861,"title":114,"titles":862,"content":863,"level":19},"/v0.0.4/reference/types#caller",[821],"type Caller struct {\n    NodeID      string\n    Certificate *x509.Certificate\n} FieldTypeDescriptionNodeIDstringCalling node's ID (from certificate CN)Certificate*x509.CertificateFull client certificate",{"id":865,"title":866,"titles":867,"content":868,"level":19},"/v0.0.4/reference/types#healthinfo","HealthInfo",[821],"type HealthInfo struct {\n    Status      HealthStatus\n    LastChecked time.Time\n    Message     string\n    Error       string\n} FieldTypeDescriptionStatusHealthStatusCurrent statusLastCheckedtime.TimeWhen last checkedMessagestringHuman-readable messageErrorstringError message if unhealthy",{"id":870,"title":871,"titles":872,"content":873,"level":19},"/v0.0.4/reference/types#healthstatus","HealthStatus",[821],"type HealthStatus string\n\nconst (\n    HealthStatusHealthy   HealthStatus = \"healthy\"\n    HealthStatusUnhealthy HealthStatus = \"unhealthy\"\n    HealthStatusUnknown   HealthStatus = \"unknown\"\n)",{"id":875,"title":876,"titles":877,"content":878,"level":19},"/v0.0.4/reference/types#healthchecker","HealthChecker",[821],"type HealthChecker interface {\n    Check(ctx context.Context) error\n    Name() string\n} Interface for health check implementations.",{"id":880,"title":881,"titles":882,"content":883,"level":19},"/v0.0.4/reference/types#tlsoptions","TLSOptions",[821],"type TLSOptions struct {\n    Source       CertificateSource\n    CertFile     string\n    KeyFile      string\n    CAFile       string\n    CertEnvVar   string\n    KeyEnvVar    string\n    CAEnvVar     string\n    VaultPath    string\n    VaultRole    string\n    VerifyChain  bool\n    AllowExpired bool\n    RequiredSANs []string\n} FieldTypeDescriptionSourceCertificateSourceWhere to load certificates fromCertFilestringPath to certificate (file source)KeyFilestringPath to private key (file source)CAFilestringPath to CA certificate (file source)CertEnvVarstringEnv var for certificate (env source)KeyEnvVarstringEnv var for private key (env source)CAEnvVarstringEnv var for CA certificate (env source)VaultPathstringVault path (vault source, future)VaultRolestringVault role (vault source, future)VerifyChainboolVerify full certificate chainAllowExpiredboolAccept expired certificatesRequiredSANs[]stringRequired Subject Alternative Names",{"id":885,"title":886,"titles":887,"content":888,"level":19},"/v0.0.4/reference/types#certificatesource","CertificateSource",[821],"type CertificateSource string\n\nconst (\n    CertSourceFile  CertificateSource = \"file\"\n    CertSourceEnv   CertificateSource = \"env\"\n    CertSourceVault CertificateSource = \"vault\"\n)",{"id":890,"title":891,"titles":892,"content":893,"level":19},"/v0.0.4/reference/types#tlsconfig","TLSConfig",[821],"type TLSConfig struct {\n    Certificate tls.Certificate\n    CertPool    *x509.CertPool\n    // internal fields\n} FieldTypeDescriptionCertificatetls.CertificateNode's certificate and keyCertPool*x509.CertPoolTrusted CA certificates Methods: GetServerTLSConfig() *tls.Config — Returns server TLS configGetClientTLSConfig(serverName string) *tls.Config — Returns client TLS config",{"id":895,"title":896,"titles":897,"content":898,"level":19},"/v0.0.4/reference/types#serviceregistrar","ServiceRegistrar",[821],"type ServiceRegistrar func(*grpc.Server) Callback to register gRPC services on the server.",{"id":900,"title":901,"titles":902,"content":903,"level":19},"/v0.0.4/reference/types#error-sentinels","Error Sentinels",[821],"var (\n    ErrNoProviders   = errors.New(\"no providers available for service\")\n    ErrNoTLSConfig   = errors.New(\"node has no TLS configuration\")\n    ErrNoPeerInfo    = errors.New(\"no peer info in context\")\n    ErrNoTLSInfo     = errors.New(\"no TLS info in peer\")\n    ErrNoCertificate = errors.New(\"no client certificate\")\n)",{"id":905,"title":32,"titles":906,"content":907,"level":19},"/v0.0.4/reference/types#next-steps",[821],"API Reference — Function signaturesConcepts — Mental models html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sfm-E, html code.shiki .sfm-E{--shiki-default:var(--shiki-variable)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}",[909],{"title":910,"path":911,"stem":912,"children":913,"page":927},"V004","/v0.0.4","v0.0.4",[914,928,941],{"title":915,"path":916,"stem":917,"children":918,"page":927},"Learn","/v0.0.4/learn","v0.0.4/1.learn",[919,921,923,925],{"title":6,"path":5,"stem":920,"description":8},"v0.0.4/1.learn/1.overview",{"title":37,"path":36,"stem":922,"description":39},"v0.0.4/1.learn/2.quickstart",{"title":80,"path":79,"stem":924,"description":82},"v0.0.4/1.learn/3.concepts",{"title":128,"path":127,"stem":926,"description":130},"v0.0.4/1.learn/4.architecture",false,{"title":929,"path":930,"stem":931,"children":932,"page":927},"Guides","/v0.0.4/guides","v0.0.4/2.guides",[933,935,937,939],{"title":215,"path":214,"stem":934,"description":217},"v0.0.4/2.guides/1.testing",{"title":273,"path":272,"stem":936,"description":275},"v0.0.4/2.guides/2.troubleshooting",{"title":375,"path":374,"stem":938,"description":377},"v0.0.4/2.guides/3.services",{"title":475,"path":474,"stem":940,"description":477},"v0.0.4/2.guides/4.certificates",{"title":942,"path":943,"stem":944,"children":945,"page":927},"Reference","/v0.0.4/reference","v0.0.4/3.reference",[946,948],{"title":574,"path":573,"stem":947,"description":576},"v0.0.4/3.reference/1.api",{"title":821,"path":820,"stem":949,"description":823},"v0.0.4/3.reference/2.types",[951],{"title":910,"path":911,"stem":912,"children":952,"page":927},[953,959,965],{"title":915,"path":916,"stem":917,"children":954,"page":927},[955,956,957,958],{"title":6,"path":5,"stem":920},{"title":37,"path":36,"stem":922},{"title":80,"path":79,"stem":924},{"title":128,"path":127,"stem":926},{"title":929,"path":930,"stem":931,"children":960,"page":927},[961,962,963,964],{"title":215,"path":214,"stem":934},{"title":273,"path":272,"stem":936},{"title":375,"path":374,"stem":938},{"title":475,"path":474,"stem":940},{"title":942,"path":943,"stem":944,"children":966,"page":927},[967,968],{"title":574,"path":573,"stem":947},{"title":821,"path":820,"stem":949},[970,2267,2340],{"id":971,"title":972,"body":973,"description":144,"extension":2260,"icon":2261,"meta":2262,"navigation":1203,"path":2263,"seo":2264,"stem":2265,"__hash__":2266},"resources/readme.md","README",{"type":974,"value":975,"toc":2249},"minimark",[976,980,1048,1051,1056,1230,1234,1251,1254,1258,1261,1903,1907,2021,2025,2063,2067,2070,2134,2138,2142,2168,2172,2198,2202,2225,2229,2237,2240,2245],[977,978,979],"h1",{"id":979},"aegis",[981,982,983,994,1002,1010,1018,1026,1033,1040],"p",{},[984,985,989],"a",{"href":986,"rel":987},"https://github.com/zoobz-io/aegis/actions/workflows/ci.yml",[988],"nofollow",[990,991],"img",{"alt":992,"src":993},"CI Status","https://github.com/zoobz-io/aegis/workflows/CI/badge.svg",[984,995,998],{"href":996,"rel":997},"https://codecov.io/gh/zoobz-io/aegis",[988],[990,999],{"alt":1000,"src":1001},"codecov","https://codecov.io/gh/zoobz-io/aegis/graph/badge.svg?branch=main",[984,1003,1006],{"href":1004,"rel":1005},"https://goreportcard.com/report/github.com/zoobz-io/aegis",[988],[990,1007],{"alt":1008,"src":1009},"Go Report Card","https://goreportcard.com/badge/github.com/zoobz-io/aegis",[984,1011,1014],{"href":1012,"rel":1013},"https://github.com/zoobz-io/aegis/security/code-scanning",[988],[990,1015],{"alt":1016,"src":1017},"CodeQL","https://github.com/zoobz-io/aegis/workflows/CodeQL/badge.svg",[984,1019,1022],{"href":1020,"rel":1021},"https://pkg.go.dev/github.com/zoobz-io/aegis",[988],[990,1023],{"alt":1024,"src":1025},"Go Reference","https://pkg.go.dev/badge/github.com/zoobz-io/aegis.svg",[984,1027,1029],{"href":1028},"LICENSE",[990,1030],{"alt":1031,"src":1032},"License","https://img.shields.io/github/license/zoobz-io/aegis",[984,1034,1036],{"href":1035},"go.mod",[990,1037],{"alt":1038,"src":1039},"Go Version","https://img.shields.io/github/go-mod/go-version/zoobz-io/aegis",[984,1041,1044],{"href":1042,"rel":1043},"https://github.com/zoobz-io/aegis/releases",[988],[990,1045],{"alt":1046,"src":1047},"Release","https://img.shields.io/github/v/release/zoobz-io/aegis",[981,1049,1050],{},"Service mesh for Go microservices — mTLS everywhere, zero configuration. Nodes discover each other, authenticate via certificates, and call domain services without managing PKI infrastructure.",[1052,1053,1055],"h2",{"id":1054},"zero-trust-by-default","Zero-Trust by Default",[1057,1058,1062],"pre",{"className":1059,"code":1060,"language":1061,"meta":144,"style":144},"language-go shiki shiki-themes","node, _ := aegis.NewNodeBuilder().\n    WithID(\"api-1\").\n    WithName(\"API Server\").\n    WithAddress(\"localhost:8443\").\n    WithServices(aegis.ServiceInfo{Name: \"identity\", Version: \"v1\"}).\n    WithCertDir(\"./certs\").\n    Build()\n\nnode.StartServer()\n// Certificates generated automatically. All connections use mTLS.\n// Other nodes can now discover this service and call it securely.\n","go",[1063,1064,1065,1095,1110,1122,1135,1176,1189,1198,1205,1217,1224],"code",{"__ignoreMap":144},[1066,1067,1069,1073,1077,1080,1083,1086,1089,1092],"span",{"class":1068,"line":9},"line",[1066,1070,1072],{"class":1071},"sh8_p","node",[1066,1074,1076],{"class":1075},"sq5bi",",",[1066,1078,1079],{"class":1071}," _",[1066,1081,1082],{"class":1071}," :=",[1066,1084,1085],{"class":1071}," aegis",[1066,1087,1088],{"class":1075},".",[1066,1090,587],{"class":1091},"s5klm",[1066,1093,1094],{"class":1075},"().\n",[1066,1096,1097,1100,1103,1107],{"class":1068,"line":19},[1066,1098,1099],{"class":1091},"    WithID",[1066,1101,1102],{"class":1075},"(",[1066,1104,1106],{"class":1105},"sxAnc","\"api-1\"",[1066,1108,1109],{"class":1075},").\n",[1066,1111,1112,1115,1117,1120],{"class":1068,"line":150},[1066,1113,1114],{"class":1091},"    WithName",[1066,1116,1102],{"class":1075},[1066,1118,1119],{"class":1105},"\"API Server\"",[1066,1121,1109],{"class":1075},[1066,1123,1125,1128,1130,1133],{"class":1068,"line":1124},4,[1066,1126,1127],{"class":1091},"    WithAddress",[1066,1129,1102],{"class":1075},[1066,1131,1132],{"class":1105},"\"localhost:8443\"",[1066,1134,1109],{"class":1075},[1066,1136,1138,1141,1143,1146,1148,1150,1153,1157,1160,1163,1165,1168,1170,1173],{"class":1068,"line":1137},5,[1066,1139,1140],{"class":1091},"    WithServices",[1066,1142,1102],{"class":1075},[1066,1144,979],{"class":1145},"sYBwO",[1066,1147,1088],{"class":1075},[1066,1149,844],{"class":1145},[1066,1151,1152],{"class":1075},"{",[1066,1154,1156],{"class":1155},"sBGCq","Name",[1066,1158,1159],{"class":1075},":",[1066,1161,1162],{"class":1105}," \"identity\"",[1066,1164,1076],{"class":1075},[1066,1166,1167],{"class":1155}," Version",[1066,1169,1159],{"class":1075},[1066,1171,1172],{"class":1105}," \"v1\"",[1066,1174,1175],{"class":1075},"}).\n",[1066,1177,1179,1182,1184,1187],{"class":1068,"line":1178},6,[1066,1180,1181],{"class":1091},"    WithCertDir",[1066,1183,1102],{"class":1075},[1066,1185,1186],{"class":1105},"\"./certs\"",[1066,1188,1109],{"class":1075},[1066,1190,1192,1195],{"class":1068,"line":1191},7,[1066,1193,1194],{"class":1091},"    Build",[1066,1196,1197],{"class":1075},"()\n",[1066,1199,1201],{"class":1068,"line":1200},8,[1066,1202,1204],{"emptyLinePlaceholder":1203},true,"\n",[1066,1206,1208,1210,1212,1215],{"class":1068,"line":1207},9,[1066,1209,1072],{"class":1071},[1066,1211,1088],{"class":1075},[1066,1213,1214],{"class":1091},"StartServer",[1066,1216,1197],{"class":1075},[1066,1218,1220],{"class":1068,"line":1219},10,[1066,1221,1223],{"class":1222},"sLkEo","// Certificates generated automatically. All connections use mTLS.\n",[1066,1225,1227],{"class":1068,"line":1226},11,[1066,1228,1229],{"class":1222},"// Other nodes can now discover this service and call it securely.\n",[1052,1231,1233],{"id":1232},"install","Install",[1057,1235,1239],{"className":1236,"code":1237,"language":1238,"meta":144,"style":144},"language-bash shiki shiki-themes","go get github.com/zoobz-io/aegis\n","bash",[1063,1240,1241],{"__ignoreMap":144},[1066,1242,1243,1245,1248],{"class":1068,"line":9},[1066,1244,1061],{"class":1091},[1066,1246,1247],{"class":1105}," get",[1066,1249,1250],{"class":1105}," github.com/zoobz-io/aegis\n",[981,1252,1253],{},"Requires Go 1.24+.",[1052,1255,1257],{"id":1256},"quick-start","Quick Start",[981,1259,1260],{},"A provider node exposes a service. A consumer node discovers and calls it.",[1057,1262,1264],{"className":1059,"code":1263,"language":1061,"meta":144,"style":144},"package main\n\nimport (\n    \"context\"\n    \"log\"\n\n    \"github.com/zoobz-io/aegis\"\n    identity \"github.com/zoobz-io/aegis/proto/identity\"\n    \"google.golang.org/grpc\"\n)\n\nfunc main() {\n    // Provider: morpheus serves identity\n    morpheus, _ := aegis.NewNodeBuilder().\n        WithID(\"morpheus-1\").\n        WithName(\"Morpheus\").\n        WithAddress(\"localhost:8443\").\n        WithServices(aegis.ServiceInfo{Name: \"identity\", Version: \"v1\"}).\n        WithServiceRegistration(func(s *grpc.Server) {\n            identity.RegisterIdentityServiceServer(s, &myIdentityServer{})\n        }).\n        WithCertDir(\"./certs\").\n        Build()\n\n    morpheus.StartServer()\n    defer morpheus.Shutdown()\n\n    // Consumer: vicky calls identity service\n    vicky, _ := aegis.NewNodeBuilder().\n        WithID(\"vicky-1\").\n        WithName(\"Vicky\").\n        WithAddress(\"localhost:9443\").\n        WithCertDir(\"./certs\").\n        Build()\n\n    pool := aegis.NewServiceClientPool(vicky)\n    defer pool.Close()\n\n    client := aegis.NewServiceClient(pool, \"identity\", \"v1\", identity.NewIdentityServiceClient)\n\n    // Get a connection (round-robin across providers)\n    ctx := context.Background()\n    identityClient, _ := client.Get(ctx)\n    resp, _ := identityClient.ValidateSession(ctx, &identity.ValidateSessionRequest{\n        Token: \"user-session-token\",\n    })\n\n    log.Printf(\"Session valid: %v, user: %s\", resp.Valid, resp.UserId)\n}\n",[1063,1265,1266,1275,1279,1288,1293,1298,1302,1307,1316,1321,1326,1330,1345,1351,1371,1384,1397,1409,1441,1473,1499,1505,1517,1525,1530,1541,1557,1562,1568,1588,1600,1612,1624,1635,1642,1647,1668,1683,1688,1727,1732,1738,1756,1783,1822,1836,1842,1847,1897],{"__ignoreMap":144},[1066,1267,1268,1272],{"class":1068,"line":9},[1066,1269,1271],{"class":1270},"sUt3r","package",[1066,1273,1274],{"class":1145}," main\n",[1066,1276,1277],{"class":1068,"line":19},[1066,1278,1204],{"emptyLinePlaceholder":1203},[1066,1280,1281,1284],{"class":1068,"line":150},[1066,1282,1283],{"class":1270},"import",[1066,1285,1287],{"class":1286},"soy-K"," (\n",[1066,1289,1290],{"class":1068,"line":1124},[1066,1291,1292],{"class":1105},"    \"context\"\n",[1066,1294,1295],{"class":1068,"line":1137},[1066,1296,1297],{"class":1105},"    \"log\"\n",[1066,1299,1300],{"class":1068,"line":1178},[1066,1301,1204],{"emptyLinePlaceholder":1203},[1066,1303,1304],{"class":1068,"line":1191},[1066,1305,1306],{"class":1105},"    \"github.com/zoobz-io/aegis\"\n",[1066,1308,1309,1313],{"class":1068,"line":1200},[1066,1310,1312],{"class":1311},"sSYET","    identity",[1066,1314,1315],{"class":1105}," \"github.com/zoobz-io/aegis/proto/identity\"\n",[1066,1317,1318],{"class":1068,"line":1207},[1066,1319,1320],{"class":1105},"    \"google.golang.org/grpc\"\n",[1066,1322,1323],{"class":1068,"line":1219},[1066,1324,1325],{"class":1286},")\n",[1066,1327,1328],{"class":1068,"line":1226},[1066,1329,1204],{"emptyLinePlaceholder":1203},[1066,1331,1333,1336,1339,1342],{"class":1068,"line":1332},12,[1066,1334,1335],{"class":1270},"func",[1066,1337,1338],{"class":1091}," main",[1066,1340,1341],{"class":1075},"()",[1066,1343,1344],{"class":1075}," {\n",[1066,1346,1348],{"class":1068,"line":1347},13,[1066,1349,1350],{"class":1222},"    // Provider: morpheus serves identity\n",[1066,1352,1354,1357,1359,1361,1363,1365,1367,1369],{"class":1068,"line":1353},14,[1066,1355,1356],{"class":1071},"    morpheus",[1066,1358,1076],{"class":1075},[1066,1360,1079],{"class":1071},[1066,1362,1082],{"class":1071},[1066,1364,1085],{"class":1071},[1066,1366,1088],{"class":1075},[1066,1368,587],{"class":1091},[1066,1370,1094],{"class":1075},[1066,1372,1374,1377,1379,1382],{"class":1068,"line":1373},15,[1066,1375,1376],{"class":1091},"        WithID",[1066,1378,1102],{"class":1075},[1066,1380,1381],{"class":1105},"\"morpheus-1\"",[1066,1383,1109],{"class":1075},[1066,1385,1387,1390,1392,1395],{"class":1068,"line":1386},16,[1066,1388,1389],{"class":1091},"        WithName",[1066,1391,1102],{"class":1075},[1066,1393,1394],{"class":1105},"\"Morpheus\"",[1066,1396,1109],{"class":1075},[1066,1398,1400,1403,1405,1407],{"class":1068,"line":1399},17,[1066,1401,1402],{"class":1091},"        WithAddress",[1066,1404,1102],{"class":1075},[1066,1406,1132],{"class":1105},[1066,1408,1109],{"class":1075},[1066,1410,1412,1415,1417,1419,1421,1423,1425,1427,1429,1431,1433,1435,1437,1439],{"class":1068,"line":1411},18,[1066,1413,1414],{"class":1091},"        WithServices",[1066,1416,1102],{"class":1075},[1066,1418,979],{"class":1145},[1066,1420,1088],{"class":1075},[1066,1422,844],{"class":1145},[1066,1424,1152],{"class":1075},[1066,1426,1156],{"class":1155},[1066,1428,1159],{"class":1075},[1066,1430,1162],{"class":1105},[1066,1432,1076],{"class":1075},[1066,1434,1167],{"class":1155},[1066,1436,1159],{"class":1075},[1066,1438,1172],{"class":1105},[1066,1440,1175],{"class":1075},[1066,1442,1444,1447,1449,1451,1453,1456,1460,1463,1465,1468,1471],{"class":1068,"line":1443},19,[1066,1445,1446],{"class":1091},"        WithServiceRegistration",[1066,1448,1102],{"class":1075},[1066,1450,1335],{"class":1270},[1066,1452,1102],{"class":1075},[1066,1454,1455],{"class":1311},"s",[1066,1457,1459],{"class":1458},"sW3Qg"," *",[1066,1461,1462],{"class":1145},"grpc",[1066,1464,1088],{"class":1075},[1066,1466,1467],{"class":1145},"Server",[1066,1469,1470],{"class":1075},")",[1066,1472,1344],{"class":1075},[1066,1474,1476,1479,1481,1484,1486,1488,1490,1493,1496],{"class":1068,"line":1475},20,[1066,1477,1478],{"class":1071},"            identity",[1066,1480,1088],{"class":1075},[1066,1482,1483],{"class":1091},"RegisterIdentityServiceServer",[1066,1485,1102],{"class":1075},[1066,1487,1455],{"class":1071},[1066,1489,1076],{"class":1075},[1066,1491,1492],{"class":1458}," &",[1066,1494,1495],{"class":1145},"myIdentityServer",[1066,1497,1498],{"class":1075},"{})\n",[1066,1500,1502],{"class":1068,"line":1501},21,[1066,1503,1504],{"class":1075},"        }).\n",[1066,1506,1508,1511,1513,1515],{"class":1068,"line":1507},22,[1066,1509,1510],{"class":1091},"        WithCertDir",[1066,1512,1102],{"class":1075},[1066,1514,1186],{"class":1105},[1066,1516,1109],{"class":1075},[1066,1518,1520,1523],{"class":1068,"line":1519},23,[1066,1521,1522],{"class":1091},"        Build",[1066,1524,1197],{"class":1075},[1066,1526,1528],{"class":1068,"line":1527},24,[1066,1529,1204],{"emptyLinePlaceholder":1203},[1066,1531,1533,1535,1537,1539],{"class":1068,"line":1532},25,[1066,1534,1356],{"class":1071},[1066,1536,1088],{"class":1075},[1066,1538,1214],{"class":1091},[1066,1540,1197],{"class":1075},[1066,1542,1544,1547,1550,1552,1555],{"class":1068,"line":1543},26,[1066,1545,1546],{"class":1458},"    defer",[1066,1548,1549],{"class":1071}," morpheus",[1066,1551,1088],{"class":1075},[1066,1553,1554],{"class":1091},"Shutdown",[1066,1556,1197],{"class":1075},[1066,1558,1560],{"class":1068,"line":1559},27,[1066,1561,1204],{"emptyLinePlaceholder":1203},[1066,1563,1565],{"class":1068,"line":1564},28,[1066,1566,1567],{"class":1222},"    // Consumer: vicky calls identity service\n",[1066,1569,1571,1574,1576,1578,1580,1582,1584,1586],{"class":1068,"line":1570},29,[1066,1572,1573],{"class":1071},"    vicky",[1066,1575,1076],{"class":1075},[1066,1577,1079],{"class":1071},[1066,1579,1082],{"class":1071},[1066,1581,1085],{"class":1071},[1066,1583,1088],{"class":1075},[1066,1585,587],{"class":1091},[1066,1587,1094],{"class":1075},[1066,1589,1591,1593,1595,1598],{"class":1068,"line":1590},30,[1066,1592,1376],{"class":1091},[1066,1594,1102],{"class":1075},[1066,1596,1597],{"class":1105},"\"vicky-1\"",[1066,1599,1109],{"class":1075},[1066,1601,1603,1605,1607,1610],{"class":1068,"line":1602},31,[1066,1604,1389],{"class":1091},[1066,1606,1102],{"class":1075},[1066,1608,1609],{"class":1105},"\"Vicky\"",[1066,1611,1109],{"class":1075},[1066,1613,1615,1617,1619,1622],{"class":1068,"line":1614},32,[1066,1616,1402],{"class":1091},[1066,1618,1102],{"class":1075},[1066,1620,1621],{"class":1105},"\"localhost:9443\"",[1066,1623,1109],{"class":1075},[1066,1625,1627,1629,1631,1633],{"class":1068,"line":1626},33,[1066,1628,1510],{"class":1091},[1066,1630,1102],{"class":1075},[1066,1632,1186],{"class":1105},[1066,1634,1109],{"class":1075},[1066,1636,1638,1640],{"class":1068,"line":1637},34,[1066,1639,1522],{"class":1091},[1066,1641,1197],{"class":1075},[1066,1643,1645],{"class":1068,"line":1644},35,[1066,1646,1204],{"emptyLinePlaceholder":1203},[1066,1648,1650,1653,1655,1657,1659,1661,1663,1666],{"class":1068,"line":1649},36,[1066,1651,1652],{"class":1071},"    pool",[1066,1654,1082],{"class":1071},[1066,1656,1085],{"class":1071},[1066,1658,1088],{"class":1075},[1066,1660,746],{"class":1091},[1066,1662,1102],{"class":1075},[1066,1664,1665],{"class":1071},"vicky",[1066,1667,1325],{"class":1075},[1066,1669,1671,1673,1676,1678,1681],{"class":1068,"line":1670},37,[1066,1672,1546],{"class":1458},[1066,1674,1675],{"class":1071}," pool",[1066,1677,1088],{"class":1075},[1066,1679,1680],{"class":1091},"Close",[1066,1682,1197],{"class":1075},[1066,1684,1686],{"class":1068,"line":1685},38,[1066,1687,1204],{"emptyLinePlaceholder":1203},[1066,1689,1691,1694,1696,1698,1700,1702,1704,1707,1709,1711,1713,1715,1717,1720,1722,1725],{"class":1068,"line":1690},39,[1066,1692,1693],{"class":1071},"    client",[1066,1695,1082],{"class":1071},[1066,1697,1085],{"class":1071},[1066,1699,1088],{"class":1075},[1066,1701,765],{"class":1091},[1066,1703,1102],{"class":1075},[1066,1705,1706],{"class":1071},"pool",[1066,1708,1076],{"class":1075},[1066,1710,1162],{"class":1105},[1066,1712,1076],{"class":1075},[1066,1714,1172],{"class":1105},[1066,1716,1076],{"class":1075},[1066,1718,1719],{"class":1071}," identity",[1066,1721,1088],{"class":1075},[1066,1723,1724],{"class":1071},"NewIdentityServiceClient",[1066,1726,1325],{"class":1075},[1066,1728,1730],{"class":1068,"line":1729},40,[1066,1731,1204],{"emptyLinePlaceholder":1203},[1066,1733,1735],{"class":1068,"line":1734},41,[1066,1736,1737],{"class":1222},"    // Get a connection (round-robin across providers)\n",[1066,1739,1741,1744,1746,1749,1751,1754],{"class":1068,"line":1740},42,[1066,1742,1743],{"class":1071},"    ctx",[1066,1745,1082],{"class":1071},[1066,1747,1748],{"class":1071}," context",[1066,1750,1088],{"class":1075},[1066,1752,1753],{"class":1091},"Background",[1066,1755,1197],{"class":1075},[1066,1757,1759,1762,1764,1766,1768,1771,1773,1776,1778,1781],{"class":1068,"line":1758},43,[1066,1760,1761],{"class":1071},"    identityClient",[1066,1763,1076],{"class":1075},[1066,1765,1079],{"class":1071},[1066,1767,1082],{"class":1071},[1066,1769,1770],{"class":1071}," client",[1066,1772,1088],{"class":1075},[1066,1774,1775],{"class":1091},"Get",[1066,1777,1102],{"class":1075},[1066,1779,1780],{"class":1071},"ctx",[1066,1782,1325],{"class":1075},[1066,1784,1786,1789,1791,1793,1795,1798,1800,1803,1805,1807,1809,1811,1814,1816,1819],{"class":1068,"line":1785},44,[1066,1787,1788],{"class":1071},"    resp",[1066,1790,1076],{"class":1075},[1066,1792,1079],{"class":1071},[1066,1794,1082],{"class":1071},[1066,1796,1797],{"class":1071}," identityClient",[1066,1799,1088],{"class":1075},[1066,1801,1802],{"class":1091},"ValidateSession",[1066,1804,1102],{"class":1075},[1066,1806,1780],{"class":1071},[1066,1808,1076],{"class":1075},[1066,1810,1492],{"class":1458},[1066,1812,1813],{"class":1145},"identity",[1066,1815,1088],{"class":1075},[1066,1817,1818],{"class":1145},"ValidateSessionRequest",[1066,1820,1821],{"class":1075},"{\n",[1066,1823,1825,1828,1830,1833],{"class":1068,"line":1824},45,[1066,1826,1827],{"class":1155},"        Token",[1066,1829,1159],{"class":1075},[1066,1831,1832],{"class":1105}," \"user-session-token\"",[1066,1834,1835],{"class":1075},",\n",[1066,1837,1839],{"class":1068,"line":1838},46,[1066,1840,1841],{"class":1075},"    })\n",[1066,1843,1845],{"class":1068,"line":1844},47,[1066,1846,1204],{"emptyLinePlaceholder":1203},[1066,1848,1850,1853,1855,1858,1860,1863,1867,1870,1873,1876,1878,1881,1883,1886,1888,1890,1892,1895],{"class":1068,"line":1849},48,[1066,1851,1852],{"class":1071},"    log",[1066,1854,1088],{"class":1075},[1066,1856,1857],{"class":1091},"Printf",[1066,1859,1102],{"class":1075},[1066,1861,1862],{"class":1105},"\"Session valid: ",[1066,1864,1866],{"class":1865},"scyPU","%v",[1066,1868,1869],{"class":1105},", user: ",[1066,1871,1872],{"class":1865},"%s",[1066,1874,1875],{"class":1105},"\"",[1066,1877,1076],{"class":1075},[1066,1879,1880],{"class":1071}," resp",[1066,1882,1088],{"class":1075},[1066,1884,1885],{"class":1071},"Valid",[1066,1887,1076],{"class":1075},[1066,1889,1880],{"class":1071},[1066,1891,1088],{"class":1075},[1066,1893,1894],{"class":1071},"UserId",[1066,1896,1325],{"class":1075},[1066,1898,1900],{"class":1068,"line":1899},49,[1066,1901,1902],{"class":1075},"}\n",[1052,1904,1906],{"id":1905},"capabilities","Capabilities",[1908,1909,1910,1926],"table",{},[1911,1912,1913],"thead",{},[1914,1915,1916,1920,1923],"tr",{},[1917,1918,1919],"th",{},"Capability",[1917,1921,1922],{},"Description",[1917,1924,1925],{},"Docs",[1927,1928,1929,1943,1956,1969,1982,1995,2008],"tbody",{},[1914,1930,1931,1935,1938],{},[1932,1933,1934],"td",{},"Node identity",[1932,1936,1937],{},"Build nodes with ID, name, type, address",[1932,1939,1940],{},[984,1941,1942],{"href":1942},"node_builder.go",[1914,1944,1945,1948,1951],{},[1932,1946,1947],{},"Automatic mTLS",[1932,1949,1950],{},"Certificates generated on first run, loaded thereafter",[1932,1952,1953],{},[984,1954,1955],{"href":1955},"tls.go",[1914,1957,1958,1961,1964],{},[1932,1959,1960],{},"Service registry",[1932,1962,1963],{},"Declare services, discover providers across mesh",[1932,1965,1966],{},[984,1967,1968],{"href":1968},"service.go",[1914,1970,1971,1974,1977],{},[1932,1972,1973],{},"Topology sync",[1932,1975,1976],{},"Nodes share topology; version-based merge",[1932,1978,1979],{},[984,1980,1981],{"href":1981},"topology.go",[1914,1983,1984,1987,1990],{},[1932,1985,1986],{},"Health checks",[1932,1988,1989],{},"Extensible health checker interface",[1932,1991,1992],{},[984,1993,1994],{"href":1994},"health.go",[1914,1996,1997,2000,2003],{},[1932,1998,1999],{},"Service client",[1932,2001,2002],{},"Connection pooling, round-robin load balancing",[1932,2004,2005],{},[984,2006,2007],{"href":2007},"client.go",[1914,2009,2010,2013,2016],{},[1932,2011,2012],{},"Caller identity",[1932,2014,2015],{},"Extract calling node from mTLS context",[1932,2017,2018],{},[984,2019,2020],{"href":2020},"context.go",[1052,2022,2024],{"id":2023},"why-aegis","Why aegis?",[2026,2027,2028,2035,2041,2047,2057],"ul",{},[2029,2030,2031,2034],"li",{},[2032,2033,1947],"strong",{}," — Nodes generate and exchange certificates on startup. No PKI infrastructure to manage.",[2029,2036,2037,2040],{},[2032,2038,2039],{},"Service discovery built-in"," — Declare services, query providers, topology syncs across the mesh.",[2029,2042,2043,2046],{},[2032,2044,2045],{},"One import"," — Node, peer connections, health checks, and gRPC server in a single package.",[2029,2048,2049,2052,2053,2056],{},[2032,2050,2051],{},"Caller identity on every request"," — ",[1063,2054,2055],{},"CallerFromContext(ctx)"," extracts the calling node from mTLS certificates.",[2029,2058,2059,2062],{},[2032,2060,2061],{},"Round-robin client pooling"," — Service clients load-balance across providers automatically.",[1052,2064,2066],{"id":2065},"the-ecosystem","The Ecosystem",[981,2068,2069],{},"aegis is the transport layer. Domain services build on top:",[1908,2071,2072,2082],{},[1911,2073,2074],{},[1914,2075,2076,2079],{},[1917,2077,2078],{},"Package",[1917,2080,2081],{},"Role",[1927,2083,2084,2096,2108,2123],{},[1914,2085,2086,2093],{},[1932,2087,2088],{},[984,2089,2092],{"href":2090,"rel":2091},"https://github.com/zoobz-io/capitan",[988],"capitan",[1932,2094,2095],{},"Event coordination within a process",[1914,2097,2098,2105],{},[1932,2099,2100],{},[984,2101,2104],{"href":2102,"rel":2103},"https://github.com/zoobz-io/herald",[988],"herald",[1932,2106,2107],{},"Bridge capitan events to message brokers (future: aegis provider)",[1914,2109,2110,2117],{},[1932,2111,2112],{},[984,2113,2116],{"href":2114,"rel":2115},"https://github.com/zoobz-io/morpheus",[988],"morpheus",[1932,2118,2119,2120],{},"Identity service — implements ",[1063,2121,2122],{},"IdentityService",[1914,2124,2125,2131],{},[1932,2126,2127],{},[984,2128,1665],{"href":2129,"rel":2130},"https://github.com/zoobz-io/vicky",[988],[1932,2132,2133],{},"Storage service — consumes identity via mesh",[1052,2135,2137],{"id":2136},"documentation","Documentation",[981,2139,2140],{},[2032,2141,915],{},[2026,2143,2144,2150,2156,2162],{},[2029,2145,2146,2149],{},[984,2147,6],{"href":2148},"docs/learn/overview"," — What aegis is and why",[2029,2151,2152,2155],{},[984,2153,37],{"href":2154},"docs/learn/quickstart"," — Build your first mesh",[2029,2157,2158,2161],{},[984,2159,80],{"href":2160},"docs/learn/concepts"," — Nodes, peers, topology, services",[2029,2163,2164,2167],{},[984,2165,128],{"href":2166},"docs/learn/architecture"," — How it works internally",[981,2169,2170],{},[2032,2171,929],{},[2026,2173,2174,2180,2186,2192],{},[2029,2175,2176,2179],{},[984,2177,215],{"href":2178},"docs/guides/testing"," — Testing code that uses aegis",[2029,2181,2182,2185],{},[984,2183,273],{"href":2184},"docs/guides/troubleshooting"," — Common errors and solutions",[2029,2187,2188,2191],{},[984,2189,375],{"href":2190},"docs/guides/services"," — Defining and consuming services",[2029,2193,2194,2197],{},[984,2195,475],{"href":2196},"docs/guides/certificates"," — Certificate management",[981,2199,2200],{},[2032,2201,942],{},[2026,2203,2204,2211,2218],{},[2029,2205,2206,2210],{},[984,2207,2209],{"href":2208},"docs/reference/api","API"," — Function signatures",[2029,2212,2213,2217],{},[984,2214,2216],{"href":2215},"docs/reference/types","Types"," — Type definitions",[2029,2219,2220,2224],{},[984,2221,2223],{"href":1020,"rel":2222},[988],"pkg.go.dev"," — Generated documentation",[1052,2226,2228],{"id":2227},"contributing","Contributing",[981,2230,2231,2232,2236],{},"Contributions welcome — see ",[984,2233,2235],{"href":2234},"CONTRIBUTING","CONTRIBUTING.md"," for guidelines.",[1052,2238,1031],{"id":2239},"license",[981,2241,2242,2243,1088],{},"MIT License — see ",[984,2244,1028],{"href":1028},[2246,2247,2248],"style",{},"html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}",{"title":144,"searchDepth":19,"depth":19,"links":2250},[2251,2252,2253,2254,2255,2256,2257,2258,2259],{"id":1054,"depth":19,"text":1055},{"id":1232,"depth":19,"text":1233},{"id":1256,"depth":19,"text":1257},{"id":1905,"depth":19,"text":1906},{"id":2023,"depth":19,"text":2024},{"id":2065,"depth":19,"text":2066},{"id":2136,"depth":19,"text":2137},{"id":2227,"depth":19,"text":2228},{"id":2239,"depth":19,"text":1031},"md","book-open",{},"/readme",{"title":972,"description":144},"readme","fFZuv0xaa84gd35h8124CeaoeJGYkqxAqQLJXoAE-F0",{"id":2268,"title":2269,"body":2270,"description":144,"extension":2260,"icon":2334,"meta":2335,"navigation":1203,"path":2336,"seo":2337,"stem":2338,"__hash__":2339},"resources/security.md","Security",{"type":974,"value":2271,"toc":2330},[2272,2276,2280,2302,2306,2309,2327],[977,2273,2275],{"id":2274},"security-policy","Security Policy",[1052,2277,2279],{"id":2278},"supported-versions","Supported Versions",[1908,2281,2282,2292],{},[1911,2283,2284],{},[1914,2285,2286,2289],{},[1917,2287,2288],{},"Version",[1917,2290,2291],{},"Supported",[1927,2293,2294],{},[1914,2295,2296,2299],{},[1932,2297,2298],{},"latest",[1932,2300,2301],{},"✅",[1052,2303,2305],{"id":2304},"reporting-a-vulnerability","Reporting a Vulnerability",[981,2307,2308],{},"If you discover a security vulnerability, please report it privately:",[2310,2311,2312,2318,2321,2324],"ol",{},[2029,2313,2314,2317],{},[2032,2315,2316],{},"Do not"," open a public issue",[2029,2319,2320],{},"Email security concerns to the maintainers",[2029,2322,2323],{},"Include a detailed description of the vulnerability",[2029,2325,2326],{},"Allow reasonable time for a fix before public disclosure",[981,2328,2329],{},"We aim to respond to security reports within 48 hours and will work with you to understand and address the issue.",{"title":144,"searchDepth":19,"depth":19,"links":2331},[2332,2333],{"id":2278,"depth":19,"text":2279},{"id":2304,"depth":19,"text":2305},"shield",{},"/security",{"title":2269,"description":144},"security","yMaWOKTuMHXAfBhs5mvOK91WbCfDcXThX0UaLNDdj5Y",{"id":2341,"title":2228,"body":2342,"description":144,"extension":2260,"icon":1063,"meta":2548,"navigation":1203,"path":2549,"seo":2550,"stem":2227,"__hash__":2551},"resources/contributing.md",{"type":974,"value":2343,"toc":2541},[2344,2348,2352,2430,2434,2441,2445,2469,2473,2493,2497,2500,2538],[977,2345,2347],{"id":2346},"contributing-to-aegis","Contributing to aegis",[1052,2349,2351],{"id":2350},"development-setup","Development Setup",[1057,2353,2355],{"className":1236,"code":2354,"language":1238,"meta":144,"style":144},"# Clone the repository\ngit clone https://github.com/zoobz-io/aegis.git\ncd aegis\n\n# Install development tools\nmake install-tools\n\n# Install git hooks\nmake install-hooks\n\n# Run checks\nmake check\n",[1063,2356,2357,2362,2373,2381,2385,2390,2398,2402,2407,2414,2418,2423],{"__ignoreMap":144},[1066,2358,2359],{"class":1068,"line":9},[1066,2360,2361],{"class":1222},"# Clone the repository\n",[1066,2363,2364,2367,2370],{"class":1068,"line":19},[1066,2365,2366],{"class":1091},"git",[1066,2368,2369],{"class":1105}," clone",[1066,2371,2372],{"class":1105}," https://github.com/zoobz-io/aegis.git\n",[1066,2374,2375,2378],{"class":1068,"line":150},[1066,2376,2377],{"class":1091},"cd",[1066,2379,2380],{"class":1105}," aegis\n",[1066,2382,2383],{"class":1068,"line":1124},[1066,2384,1204],{"emptyLinePlaceholder":1203},[1066,2386,2387],{"class":1068,"line":1137},[1066,2388,2389],{"class":1222},"# Install development tools\n",[1066,2391,2392,2395],{"class":1068,"line":1178},[1066,2393,2394],{"class":1091},"make",[1066,2396,2397],{"class":1105}," install-tools\n",[1066,2399,2400],{"class":1068,"line":1191},[1066,2401,1204],{"emptyLinePlaceholder":1203},[1066,2403,2404],{"class":1068,"line":1200},[1066,2405,2406],{"class":1222},"# Install git hooks\n",[1066,2408,2409,2411],{"class":1068,"line":1207},[1066,2410,2394],{"class":1091},[1066,2412,2413],{"class":1105}," install-hooks\n",[1066,2415,2416],{"class":1068,"line":1219},[1066,2417,1204],{"emptyLinePlaceholder":1203},[1066,2419,2420],{"class":1068,"line":1226},[1066,2421,2422],{"class":1222},"# Run checks\n",[1066,2424,2425,2427],{"class":1068,"line":1332},[1066,2426,2394],{"class":1091},[1066,2428,2429],{"class":1105}," check\n",[1052,2431,2433],{"id":2432},"available-commands","Available Commands",[981,2435,2436,2437,2440],{},"Run ",[1063,2438,2439],{},"make help"," to see all available commands.",[1052,2442,2444],{"id":2443},"workflow","Workflow",[2310,2446,2447,2450,2456,2459,2466],{},[2029,2448,2449],{},"Fork the repository",[2029,2451,2452,2453],{},"Create a feature branch from ",[1063,2454,2455],{},"main",[2029,2457,2458],{},"Make your changes",[2029,2460,2461,2462,2465],{},"Ensure ",[1063,2463,2464],{},"make check"," passes",[2029,2467,2468],{},"Submit a pull request",[1052,2470,2472],{"id":2471},"code-standards","Code Standards",[2026,2474,2475,2481,2487,2490],{},[2029,2476,2477,2478,1470],{},"All code must pass linting (",[1063,2479,2480],{},"make lint",[2029,2482,2483,2484,1470],{},"All tests must pass (",[1063,2485,2486],{},"make test",[2029,2488,2489],{},"New code requires tests (80% patch coverage)",[2029,2491,2492],{},"Follow existing code conventions",[1052,2494,2496],{"id":2495},"commit-messages","Commit Messages",[981,2498,2499],{},"Use conventional commits:",[2026,2501,2502,2508,2514,2520,2526,2532],{},[2029,2503,2504,2507],{},[1063,2505,2506],{},"feat:"," new features",[2029,2509,2510,2513],{},[1063,2511,2512],{},"fix:"," bug fixes",[2029,2515,2516,2519],{},[1063,2517,2518],{},"docs:"," documentation",[2029,2521,2522,2525],{},[1063,2523,2524],{},"test:"," test changes",[2029,2527,2528,2531],{},[1063,2529,2530],{},"refactor:"," code refactoring",[2029,2533,2534,2537],{},[1063,2535,2536],{},"perf:"," performance improvements",[2246,2539,2540],{},"html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":144,"searchDepth":19,"depth":19,"links":2542},[2543,2544,2545,2546,2547],{"id":2350,"depth":19,"text":2351},{"id":2432,"depth":19,"text":2433},{"id":2443,"depth":19,"text":2444},{"id":2471,"depth":19,"text":2472},{"id":2495,"depth":19,"text":2496},{},"/contributing",{"title":2228,"description":144},"hE8SEunq2GJHzL75bpcr7CPWkt4LfXiD9g44cQ2mlO4",{"id":2553,"title":574,"author":2554,"body":2555,"description":576,"extension":2260,"meta":4640,"navigation":1203,"path":573,"published":4641,"readtime":4642,"seo":4643,"stem":947,"tags":4644,"updated":4641,"__hash__":4647},"aegis/v0.0.4/3.reference/1.api.md","zoobzio",{"type":974,"value":2556,"toc":4580},[2557,2560,2562,2565,2569,2588,2591,2604,2609,2629,2632,2672,2675,2678,2713,2716,2719,2755,2760,2763,2798,2801,2804,2842,2845,2848,2884,2887,2891,2947,2950,2985,2988,2991,3028,3034,3037,3074,3077,3080,3082,3085,3113,3116,3119,3145,3148,3151,3185,3188,3191,3224,3227,3230,3273,3276,3279,3311,3314,3317,3371,3374,3377,3430,3433,3436,3478,3481,3484,3520,3523,3526,3572,3575,3578,3622,3625,3628,3655,3658,3660,3663,3666,3700,3703,3706,3739,3742,3745,3785,3788,3791,3820,3823,3826,3865,3868,3871,3905,3908,3911,3938,3941,3943,3946,3949,3976,3979,3982,4045,4048,4053,4067,4070,4096,4099,4101,4104,4107,4182,4185,4189,4229,4232,4282,4285,4287,4290,4293,4329,4332,4336,4356,4359,4388,4391,4393,4396,4399,4418,4421,4424,4468,4471,4473,4476,4479,4516,4519,4522,4556,4559,4561,4564,4577],[977,2558,574],{"id":2559},"api-reference",[981,2561,580],{},[1052,2563,583],{"id":2564},"node-builder",[2566,2567,587],"h3",{"id":2568},"newnodebuilder",[1057,2570,2572],{"className":1059,"code":2571,"language":1061,"meta":144,"style":144},"func NewNodeBuilder() *NodeBuilder\n",[1063,2573,2574],{"__ignoreMap":144},[1066,2575,2576,2578,2581,2583,2585],{"class":1068,"line":9},[1066,2577,1335],{"class":1270},[1066,2579,2580],{"class":1091}," NewNodeBuilder",[1066,2582,1341],{"class":1075},[1066,2584,1459],{"class":1458},[1066,2586,2587],{"class":1145},"NodeBuilder\n",[981,2589,2590],{},"Creates a new node builder with defaults:",[2026,2592,2593,2599],{},[2029,2594,2595,2596],{},"Type: ",[1063,2597,2598],{},"NodeTypeGeneric",[2029,2600,2601,2602],{},"CertDir: ",[1063,2603,1186],{},[981,2605,2606],{},[2032,2607,2608],{},"Example:",[1057,2610,2612],{"className":1059,"code":2611,"language":1061,"meta":144,"style":144},"builder := aegis.NewNodeBuilder()\n",[1063,2613,2614],{"__ignoreMap":144},[1066,2615,2616,2619,2621,2623,2625,2627],{"class":1068,"line":9},[1066,2617,2618],{"class":1071},"builder",[1066,2620,1082],{"class":1071},[1066,2622,1085],{"class":1071},[1066,2624,1088],{"class":1075},[1066,2626,587],{"class":1091},[1066,2628,1197],{"class":1075},[2566,2630,592],{"id":2631},"nodebuilderwithid",[1057,2633,2635],{"className":1059,"code":2634,"language":1061,"meta":144,"style":144},"func (nb *NodeBuilder) WithID(id string) *NodeBuilder\n",[1063,2636,2637],{"__ignoreMap":144},[1066,2638,2639,2641,2644,2647,2650,2653,2655,2658,2660,2663,2666,2668,2670],{"class":1068,"line":9},[1066,2640,1335],{"class":1270},[1066,2642,2643],{"class":1075}," (",[1066,2645,2646],{"class":1311},"nb ",[1066,2648,2649],{"class":1458},"*",[1066,2651,2652],{"class":1145},"NodeBuilder",[1066,2654,1470],{"class":1075},[1066,2656,2657],{"class":1091}," WithID",[1066,2659,1102],{"class":1075},[1066,2661,2662],{"class":1311},"id",[1066,2664,2665],{"class":1145}," string",[1066,2667,1470],{"class":1075},[1066,2669,1459],{"class":1458},[1066,2671,2587],{"class":1145},[981,2673,2674],{},"Sets the node ID. Required.",[2566,2676,597],{"id":2677},"nodebuilderwithname",[1057,2679,2681],{"className":1059,"code":2680,"language":1061,"meta":144,"style":144},"func (nb *NodeBuilder) WithName(name string) *NodeBuilder\n",[1063,2682,2683],{"__ignoreMap":144},[1066,2684,2685,2687,2689,2691,2693,2695,2697,2700,2702,2705,2707,2709,2711],{"class":1068,"line":9},[1066,2686,1335],{"class":1270},[1066,2688,2643],{"class":1075},[1066,2690,2646],{"class":1311},[1066,2692,2649],{"class":1458},[1066,2694,2652],{"class":1145},[1066,2696,1470],{"class":1075},[1066,2698,2699],{"class":1091}," WithName",[1066,2701,1102],{"class":1075},[1066,2703,2704],{"class":1311},"name",[1066,2706,2665],{"class":1145},[1066,2708,1470],{"class":1075},[1066,2710,1459],{"class":1458},[1066,2712,2587],{"class":1145},[981,2714,2715],{},"Sets the node name. Required.",[2566,2717,602],{"id":2718},"nodebuilderwithtype",[1057,2720,2722],{"className":1059,"code":2721,"language":1061,"meta":144,"style":144},"func (nb *NodeBuilder) WithType(nodeType NodeType) *NodeBuilder\n",[1063,2723,2724],{"__ignoreMap":144},[1066,2725,2726,2728,2730,2732,2734,2736,2738,2741,2743,2746,2749,2751,2753],{"class":1068,"line":9},[1066,2727,1335],{"class":1270},[1066,2729,2643],{"class":1075},[1066,2731,2646],{"class":1311},[1066,2733,2649],{"class":1458},[1066,2735,2652],{"class":1145},[1066,2737,1470],{"class":1075},[1066,2739,2740],{"class":1091}," WithType",[1066,2742,1102],{"class":1075},[1066,2744,2745],{"class":1311},"nodeType",[1066,2747,2748],{"class":1145}," NodeType",[1066,2750,1470],{"class":1075},[1066,2752,1459],{"class":1458},[1066,2754,2587],{"class":1145},[981,2756,2757,2758,1088],{},"Sets the node type. Optional, defaults to ",[1063,2759,2598],{},[2566,2761,607],{"id":2762},"nodebuilderwithaddress",[1057,2764,2766],{"className":1059,"code":2765,"language":1061,"meta":144,"style":144},"func (nb *NodeBuilder) WithAddress(address string) *NodeBuilder\n",[1063,2767,2768],{"__ignoreMap":144},[1066,2769,2770,2772,2774,2776,2778,2780,2782,2785,2787,2790,2792,2794,2796],{"class":1068,"line":9},[1066,2771,1335],{"class":1270},[1066,2773,2643],{"class":1075},[1066,2775,2646],{"class":1311},[1066,2777,2649],{"class":1458},[1066,2779,2652],{"class":1145},[1066,2781,1470],{"class":1075},[1066,2783,2784],{"class":1091}," WithAddress",[1066,2786,1102],{"class":1075},[1066,2788,2789],{"class":1311},"address",[1066,2791,2665],{"class":1145},[1066,2793,1470],{"class":1075},[1066,2795,1459],{"class":1458},[1066,2797,2587],{"class":1145},[981,2799,2800],{},"Sets the node address (host:port). Required.",[2566,2802,612],{"id":2803},"nodebuilderwithservices",[1057,2805,2807],{"className":1059,"code":2806,"language":1061,"meta":144,"style":144},"func (nb *NodeBuilder) WithServices(services ...ServiceInfo) *NodeBuilder\n",[1063,2808,2809],{"__ignoreMap":144},[1066,2810,2811,2813,2815,2817,2819,2821,2823,2826,2828,2831,2834,2836,2838,2840],{"class":1068,"line":9},[1066,2812,1335],{"class":1270},[1066,2814,2643],{"class":1075},[1066,2816,2646],{"class":1311},[1066,2818,2649],{"class":1458},[1066,2820,2652],{"class":1145},[1066,2822,1470],{"class":1075},[1066,2824,2825],{"class":1091}," WithServices",[1066,2827,1102],{"class":1075},[1066,2829,2830],{"class":1311},"services",[1066,2832,2833],{"class":1458}," ...",[1066,2835,844],{"class":1145},[1066,2837,1470],{"class":1075},[1066,2839,1459],{"class":1458},[1066,2841,2587],{"class":1145},[981,2843,2844],{},"Declares services this node provides. Optional.",[2566,2846,617],{"id":2847},"nodebuilderwithserviceregistration",[1057,2849,2851],{"className":1059,"code":2850,"language":1061,"meta":144,"style":144},"func (nb *NodeBuilder) WithServiceRegistration(r ServiceRegistrar) *NodeBuilder\n",[1063,2852,2853],{"__ignoreMap":144},[1066,2854,2855,2857,2859,2861,2863,2865,2867,2870,2872,2875,2878,2880,2882],{"class":1068,"line":9},[1066,2856,1335],{"class":1270},[1066,2858,2643],{"class":1075},[1066,2860,2646],{"class":1311},[1066,2862,2649],{"class":1458},[1066,2864,2652],{"class":1145},[1066,2866,1470],{"class":1075},[1066,2868,2869],{"class":1091}," WithServiceRegistration",[1066,2871,1102],{"class":1075},[1066,2873,2874],{"class":1311},"r",[1066,2876,2877],{"class":1145}," ServiceRegistrar",[1066,2879,1470],{"class":1075},[1066,2881,1459],{"class":1458},[1066,2883,2587],{"class":1145},[981,2885,2886],{},"Adds a callback to register gRPC services. Called when server starts.",[981,2888,2889],{},[2032,2890,2608],{},[1057,2892,2894],{"className":1059,"code":2893,"language":1061,"meta":144,"style":144},"WithServiceRegistration(func(s *grpc.Server) {\n    identity.RegisterIdentityServiceServer(s, &myServer{})\n})\n",[1063,2895,2896,2921,2942],{"__ignoreMap":144},[1066,2897,2898,2901,2903,2905,2907,2909,2911,2913,2915,2917,2919],{"class":1068,"line":9},[1066,2899,2900],{"class":1091},"WithServiceRegistration",[1066,2902,1102],{"class":1075},[1066,2904,1335],{"class":1270},[1066,2906,1102],{"class":1075},[1066,2908,1455],{"class":1311},[1066,2910,1459],{"class":1458},[1066,2912,1462],{"class":1145},[1066,2914,1088],{"class":1075},[1066,2916,1467],{"class":1145},[1066,2918,1470],{"class":1075},[1066,2920,1344],{"class":1075},[1066,2922,2923,2925,2927,2929,2931,2933,2935,2937,2940],{"class":1068,"line":19},[1066,2924,1312],{"class":1071},[1066,2926,1088],{"class":1075},[1066,2928,1483],{"class":1091},[1066,2930,1102],{"class":1075},[1066,2932,1455],{"class":1071},[1066,2934,1076],{"class":1075},[1066,2936,1492],{"class":1458},[1066,2938,2939],{"class":1145},"myServer",[1066,2941,1498],{"class":1075},[1066,2943,2944],{"class":1068,"line":150},[1066,2945,2946],{"class":1075},"})\n",[2566,2948,622],{"id":2949},"nodebuilderwithcertdir",[1057,2951,2953],{"className":1059,"code":2952,"language":1061,"meta":144,"style":144},"func (nb *NodeBuilder) WithCertDir(certDir string) *NodeBuilder\n",[1063,2954,2955],{"__ignoreMap":144},[1066,2956,2957,2959,2961,2963,2965,2967,2969,2972,2974,2977,2979,2981,2983],{"class":1068,"line":9},[1066,2958,1335],{"class":1270},[1066,2960,2643],{"class":1075},[1066,2962,2646],{"class":1311},[1066,2964,2649],{"class":1458},[1066,2966,2652],{"class":1145},[1066,2968,1470],{"class":1075},[1066,2970,2971],{"class":1091}," WithCertDir",[1066,2973,1102],{"class":1075},[1066,2975,2976],{"class":1311},"certDir",[1066,2978,2665],{"class":1145},[1066,2980,1470],{"class":1075},[1066,2982,1459],{"class":1458},[1066,2984,2587],{"class":1145},[981,2986,2987],{},"Sets the certificate directory. Certificates are generated if missing.",[2566,2989,627],{"id":2990},"nodebuilderwithtlsoptions",[1057,2992,2994],{"className":1059,"code":2993,"language":1061,"meta":144,"style":144},"func (nb *NodeBuilder) WithTLSOptions(opts *TLSOptions) *NodeBuilder\n",[1063,2995,2996],{"__ignoreMap":144},[1066,2997,2998,3000,3002,3004,3006,3008,3010,3013,3015,3018,3020,3022,3024,3026],{"class":1068,"line":9},[1066,2999,1335],{"class":1270},[1066,3001,2643],{"class":1075},[1066,3003,2646],{"class":1311},[1066,3005,2649],{"class":1458},[1066,3007,2652],{"class":1145},[1066,3009,1470],{"class":1075},[1066,3011,3012],{"class":1091}," WithTLSOptions",[1066,3014,1102],{"class":1075},[1066,3016,3017],{"class":1311},"opts",[1066,3019,1459],{"class":1458},[1066,3021,881],{"class":1145},[1066,3023,1470],{"class":1075},[1066,3025,1459],{"class":1458},[1066,3027,2587],{"class":1145},[981,3029,3030,3031,1088],{},"Sets custom TLS options. Overrides ",[1063,3032,3033],{},"WithCertDir",[2566,3035,632],{"id":3036},"nodebuilderbuild",[1057,3038,3040],{"className":1059,"code":3039,"language":1061,"meta":144,"style":144},"func (nb *NodeBuilder) Build() (*Node, error)\n",[1063,3041,3042],{"__ignoreMap":144},[1066,3043,3044,3046,3048,3050,3052,3054,3056,3059,3061,3063,3065,3067,3069,3072],{"class":1068,"line":9},[1066,3045,1335],{"class":1270},[1066,3047,2643],{"class":1075},[1066,3049,2646],{"class":1311},[1066,3051,2649],{"class":1458},[1066,3053,2652],{"class":1145},[1066,3055,1470],{"class":1075},[1066,3057,3058],{"class":1091}," Build",[1066,3060,1341],{"class":1075},[1066,3062,2643],{"class":1075},[1066,3064,2649],{"class":1458},[1066,3066,89],{"class":1145},[1066,3068,1076],{"class":1075},[1066,3070,3071],{"class":1145}," error",[1066,3073,1325],{"class":1075},[981,3075,3076],{},"Creates the node. Returns error if required fields missing or TLS setup fails.",[3078,3079],"hr",{},[1052,3081,89],{"id":1072},[2566,3083,640],{"id":3084},"nodestartserver",[1057,3086,3088],{"className":1059,"code":3087,"language":1061,"meta":144,"style":144},"func (n *Node) StartServer() error\n",[1063,3089,3090],{"__ignoreMap":144},[1066,3091,3092,3094,3096,3099,3101,3103,3105,3108,3110],{"class":1068,"line":9},[1066,3093,1335],{"class":1270},[1066,3095,2643],{"class":1075},[1066,3097,3098],{"class":1311},"n ",[1066,3100,2649],{"class":1458},[1066,3102,89],{"class":1145},[1066,3104,1470],{"class":1075},[1066,3106,3107],{"class":1091}," StartServer",[1066,3109,1341],{"class":1075},[1066,3111,3112],{"class":1145}," error\n",[981,3114,3115],{},"Starts the gRPC server. Returns error if TLS not configured or bind fails.",[2566,3117,645],{"id":3118},"nodeshutdown",[1057,3120,3122],{"className":1059,"code":3121,"language":1061,"meta":144,"style":144},"func (n *Node) Shutdown() error\n",[1063,3123,3124],{"__ignoreMap":144},[1066,3125,3126,3128,3130,3132,3134,3136,3138,3141,3143],{"class":1068,"line":9},[1066,3127,1335],{"class":1270},[1066,3129,2643],{"class":1075},[1066,3131,3098],{"class":1311},[1066,3133,2649],{"class":1458},[1066,3135,89],{"class":1145},[1066,3137,1470],{"class":1075},[1066,3139,3140],{"class":1091}," Shutdown",[1066,3142,1341],{"class":1075},[1066,3144,3112],{"class":1145},[981,3146,3147],{},"Gracefully shuts down server and closes peer connections.",[2566,3149,650],{"id":3150},"nodeaddpeer",[1057,3152,3154],{"className":1059,"code":3153,"language":1061,"meta":144,"style":144},"func (n *Node) AddPeer(info PeerInfo) error\n",[1063,3155,3156],{"__ignoreMap":144},[1066,3157,3158,3160,3162,3164,3166,3168,3170,3173,3175,3178,3181,3183],{"class":1068,"line":9},[1066,3159,1335],{"class":1270},[1066,3161,2643],{"class":1075},[1066,3163,3098],{"class":1311},[1066,3165,2649],{"class":1458},[1066,3167,89],{"class":1145},[1066,3169,1470],{"class":1075},[1066,3171,3172],{"class":1091}," AddPeer",[1066,3174,1102],{"class":1075},[1066,3176,3177],{"class":1311},"info",[1066,3179,3180],{"class":1145}," PeerInfo",[1066,3182,1470],{"class":1075},[1066,3184,3112],{"class":1145},[981,3186,3187],{},"Adds a peer connection. Connection established on first use.",[2566,3189,655],{"id":3190},"noderemovepeer",[1057,3192,3194],{"className":1059,"code":3193,"language":1061,"meta":144,"style":144},"func (n *Node) RemovePeer(peerID string) error\n",[1063,3195,3196],{"__ignoreMap":144},[1066,3197,3198,3200,3202,3204,3206,3208,3210,3213,3215,3218,3220,3222],{"class":1068,"line":9},[1066,3199,1335],{"class":1270},[1066,3201,2643],{"class":1075},[1066,3203,3098],{"class":1311},[1066,3205,2649],{"class":1458},[1066,3207,89],{"class":1145},[1066,3209,1470],{"class":1075},[1066,3211,3212],{"class":1091}," RemovePeer",[1066,3214,1102],{"class":1075},[1066,3216,3217],{"class":1311},"peerID",[1066,3219,2665],{"class":1145},[1066,3221,1470],{"class":1075},[1066,3223,3112],{"class":1145},[981,3225,3226],{},"Removes a peer and closes its connection.",[2566,3228,660],{"id":3229},"nodegetpeer",[1057,3231,3233],{"className":1059,"code":3232,"language":1061,"meta":144,"style":144},"func (n *Node) GetPeer(peerID string) (*Peer, bool)\n",[1063,3234,3235],{"__ignoreMap":144},[1066,3236,3237,3239,3241,3243,3245,3247,3249,3252,3254,3256,3258,3260,3262,3264,3266,3268,3271],{"class":1068,"line":9},[1066,3238,1335],{"class":1270},[1066,3240,2643],{"class":1075},[1066,3242,3098],{"class":1311},[1066,3244,2649],{"class":1458},[1066,3246,89],{"class":1145},[1066,3248,1470],{"class":1075},[1066,3250,3251],{"class":1091}," GetPeer",[1066,3253,1102],{"class":1075},[1066,3255,3217],{"class":1311},[1066,3257,2665],{"class":1145},[1066,3259,1470],{"class":1075},[1066,3261,2643],{"class":1075},[1066,3263,2649],{"class":1458},[1066,3265,94],{"class":1145},[1066,3267,1076],{"class":1075},[1066,3269,3270],{"class":1145}," bool",[1066,3272,1325],{"class":1075},[981,3274,3275],{},"Returns a peer by ID.",[2566,3277,665],{"id":3278},"nodegetallpeers",[1057,3280,3282],{"className":1059,"code":3281,"language":1061,"meta":144,"style":144},"func (n *Node) GetAllPeers() []*Peer\n",[1063,3283,3284],{"__ignoreMap":144},[1066,3285,3286,3288,3290,3292,3294,3296,3298,3301,3303,3306,3308],{"class":1068,"line":9},[1066,3287,1335],{"class":1270},[1066,3289,2643],{"class":1075},[1066,3291,3098],{"class":1311},[1066,3293,2649],{"class":1458},[1066,3295,89],{"class":1145},[1066,3297,1470],{"class":1075},[1066,3299,3300],{"class":1091}," GetAllPeers",[1066,3302,1341],{"class":1075},[1066,3304,3305],{"class":1075}," []",[1066,3307,2649],{"class":1458},[1066,3309,3310],{"class":1145},"Peer\n",[981,3312,3313],{},"Returns all connected peers.",[2566,3315,670],{"id":3316},"nodepingpeer",[1057,3318,3320],{"className":1059,"code":3319,"language":1061,"meta":144,"style":144},"func (n *Node) PingPeer(ctx context.Context, peerID string) (*PingResponse, error)\n",[1063,3321,3322],{"__ignoreMap":144},[1066,3323,3324,3326,3328,3330,3332,3334,3336,3339,3341,3343,3345,3347,3349,3351,3354,3356,3358,3360,3362,3365,3367,3369],{"class":1068,"line":9},[1066,3325,1335],{"class":1270},[1066,3327,2643],{"class":1075},[1066,3329,3098],{"class":1311},[1066,3331,2649],{"class":1458},[1066,3333,89],{"class":1145},[1066,3335,1470],{"class":1075},[1066,3337,3338],{"class":1091}," PingPeer",[1066,3340,1102],{"class":1075},[1066,3342,1780],{"class":1311},[1066,3344,1748],{"class":1145},[1066,3346,1088],{"class":1075},[1066,3348,775],{"class":1145},[1066,3350,1076],{"class":1075},[1066,3352,3353],{"class":1311}," peerID",[1066,3355,2665],{"class":1145},[1066,3357,1470],{"class":1075},[1066,3359,2643],{"class":1075},[1066,3361,2649],{"class":1458},[1066,3363,3364],{"class":1145},"PingResponse",[1066,3366,1076],{"class":1075},[1066,3368,3071],{"class":1145},[1066,3370,1325],{"class":1075},[981,3372,3373],{},"Pings a peer and returns response with latency.",[2566,3375,675],{"id":3376},"nodegetpeerhealth",[1057,3378,3380],{"className":1059,"code":3379,"language":1061,"meta":144,"style":144},"func (n *Node) GetPeerHealth(ctx context.Context, peerID string) (*HealthResponse, error)\n",[1063,3381,3382],{"__ignoreMap":144},[1066,3383,3384,3386,3388,3390,3392,3394,3396,3399,3401,3403,3405,3407,3409,3411,3413,3415,3417,3419,3421,3424,3426,3428],{"class":1068,"line":9},[1066,3385,1335],{"class":1270},[1066,3387,2643],{"class":1075},[1066,3389,3098],{"class":1311},[1066,3391,2649],{"class":1458},[1066,3393,89],{"class":1145},[1066,3395,1470],{"class":1075},[1066,3397,3398],{"class":1091}," GetPeerHealth",[1066,3400,1102],{"class":1075},[1066,3402,1780],{"class":1311},[1066,3404,1748],{"class":1145},[1066,3406,1088],{"class":1075},[1066,3408,775],{"class":1145},[1066,3410,1076],{"class":1075},[1066,3412,3353],{"class":1311},[1066,3414,2665],{"class":1145},[1066,3416,1470],{"class":1075},[1066,3418,2643],{"class":1075},[1066,3420,2649],{"class":1458},[1066,3422,3423],{"class":1145},"HealthResponse",[1066,3425,1076],{"class":1075},[1066,3427,3071],{"class":1145},[1066,3429,1325],{"class":1075},[981,3431,3432],{},"Gets health status from a peer.",[2566,3434,680],{"id":3435},"nodesynctopology",[1057,3437,3439],{"className":1059,"code":3438,"language":1061,"meta":144,"style":144},"func (n *Node) SyncTopology(ctx context.Context, peerID string) error\n",[1063,3440,3441],{"__ignoreMap":144},[1066,3442,3443,3445,3447,3449,3451,3453,3455,3458,3460,3462,3464,3466,3468,3470,3472,3474,3476],{"class":1068,"line":9},[1066,3444,1335],{"class":1270},[1066,3446,2643],{"class":1075},[1066,3448,3098],{"class":1311},[1066,3450,2649],{"class":1458},[1066,3452,89],{"class":1145},[1066,3454,1470],{"class":1075},[1066,3456,3457],{"class":1091}," SyncTopology",[1066,3459,1102],{"class":1075},[1066,3461,1780],{"class":1311},[1066,3463,1748],{"class":1145},[1066,3465,1088],{"class":1075},[1066,3467,775],{"class":1145},[1066,3469,1076],{"class":1075},[1066,3471,3353],{"class":1311},[1066,3473,2665],{"class":1145},[1066,3475,1470],{"class":1075},[1066,3477,3112],{"class":1145},[981,3479,3480],{},"Synchronizes topology with a specific peer.",[2566,3482,685],{"id":3483},"nodesynctopologywithallpeers",[1057,3485,3487],{"className":1059,"code":3486,"language":1061,"meta":144,"style":144},"func (n *Node) SyncTopologyWithAllPeers(ctx context.Context) error\n",[1063,3488,3489],{"__ignoreMap":144},[1066,3490,3491,3493,3495,3497,3499,3501,3503,3506,3508,3510,3512,3514,3516,3518],{"class":1068,"line":9},[1066,3492,1335],{"class":1270},[1066,3494,2643],{"class":1075},[1066,3496,3098],{"class":1311},[1066,3498,2649],{"class":1458},[1066,3500,89],{"class":1145},[1066,3502,1470],{"class":1075},[1066,3504,3505],{"class":1091}," SyncTopologyWithAllPeers",[1066,3507,1102],{"class":1075},[1066,3509,1780],{"class":1311},[1066,3511,1748],{"class":1145},[1066,3513,1088],{"class":1075},[1066,3515,775],{"class":1145},[1066,3517,1470],{"class":1075},[1066,3519,3112],{"class":1145},[981,3521,3522],{},"Synchronizes topology with all connected peers.",[2566,3524,690],{"id":3525},"nodesethealth",[1057,3527,3529],{"className":1059,"code":3528,"language":1061,"meta":144,"style":144},"func (n *Node) SetHealth(status HealthStatus, message string, err error)\n",[1063,3530,3531],{"__ignoreMap":144},[1066,3532,3533,3535,3537,3539,3541,3543,3545,3548,3550,3553,3556,3558,3561,3563,3565,3568,3570],{"class":1068,"line":9},[1066,3534,1335],{"class":1270},[1066,3536,2643],{"class":1075},[1066,3538,3098],{"class":1311},[1066,3540,2649],{"class":1458},[1066,3542,89],{"class":1145},[1066,3544,1470],{"class":1075},[1066,3546,3547],{"class":1091}," SetHealth",[1066,3549,1102],{"class":1075},[1066,3551,3552],{"class":1311},"status",[1066,3554,3555],{"class":1145}," HealthStatus",[1066,3557,1076],{"class":1075},[1066,3559,3560],{"class":1311}," message",[1066,3562,2665],{"class":1145},[1066,3564,1076],{"class":1075},[1066,3566,3567],{"class":1311}," err",[1066,3569,3071],{"class":1145},[1066,3571,1325],{"class":1075},[981,3573,3574],{},"Sets the node's health status.",[2566,3576,695],{"id":3577},"nodecheckhealth",[1057,3579,3581],{"className":1059,"code":3580,"language":1061,"meta":144,"style":144},"func (n *Node) CheckHealth(ctx context.Context, checker HealthChecker) error\n",[1063,3582,3583],{"__ignoreMap":144},[1066,3584,3585,3587,3589,3591,3593,3595,3597,3600,3602,3604,3606,3608,3610,3612,3615,3618,3620],{"class":1068,"line":9},[1066,3586,1335],{"class":1270},[1066,3588,2643],{"class":1075},[1066,3590,3098],{"class":1311},[1066,3592,2649],{"class":1458},[1066,3594,89],{"class":1145},[1066,3596,1470],{"class":1075},[1066,3598,3599],{"class":1091}," CheckHealth",[1066,3601,1102],{"class":1075},[1066,3603,1780],{"class":1311},[1066,3605,1748],{"class":1145},[1066,3607,1088],{"class":1075},[1066,3609,775],{"class":1145},[1066,3611,1076],{"class":1075},[1066,3613,3614],{"class":1311}," checker",[1066,3616,3617],{"class":1145}," HealthChecker",[1066,3619,1470],{"class":1075},[1066,3621,3112],{"class":1145},[981,3623,3624],{},"Runs a health checker and updates status.",[2566,3626,700],{"id":3627},"nodeishealthy",[1057,3629,3631],{"className":1059,"code":3630,"language":1061,"meta":144,"style":144},"func (n *Node) IsHealthy() bool\n",[1063,3632,3633],{"__ignoreMap":144},[1066,3634,3635,3637,3639,3641,3643,3645,3647,3650,3652],{"class":1068,"line":9},[1066,3636,1335],{"class":1270},[1066,3638,2643],{"class":1075},[1066,3640,3098],{"class":1311},[1066,3642,2649],{"class":1458},[1066,3644,89],{"class":1145},[1066,3646,1470],{"class":1075},[1066,3648,3649],{"class":1091}," IsHealthy",[1066,3651,1341],{"class":1075},[1066,3653,3654],{"class":1145}," bool\n",[981,3656,3657],{},"Returns true if node status is healthy.",[3078,3659],{},[1052,3661,99],{"id":3662},"topology",[2566,3664,708],{"id":3665},"topologyaddnode",[1057,3667,3669],{"className":1059,"code":3668,"language":1061,"meta":144,"style":144},"func (t *Topology) AddNode(info NodeInfo) error\n",[1063,3670,3671],{"__ignoreMap":144},[1066,3672,3673,3675,3677,3680,3682,3684,3686,3689,3691,3693,3696,3698],{"class":1068,"line":9},[1066,3674,1335],{"class":1270},[1066,3676,2643],{"class":1075},[1066,3678,3679],{"class":1311},"t ",[1066,3681,2649],{"class":1458},[1066,3683,99],{"class":1145},[1066,3685,1470],{"class":1075},[1066,3687,3688],{"class":1091}," AddNode",[1066,3690,1102],{"class":1075},[1066,3692,3177],{"class":1311},[1066,3694,3695],{"class":1145}," NodeInfo",[1066,3697,1470],{"class":1075},[1066,3699,3112],{"class":1145},[981,3701,3702],{},"Adds a node to topology. Error if node already exists.",[2566,3704,713],{"id":3705},"topologyremovenode",[1057,3707,3709],{"className":1059,"code":3708,"language":1061,"meta":144,"style":144},"func (t *Topology) RemoveNode(nodeID string) error\n",[1063,3710,3711],{"__ignoreMap":144},[1066,3712,3713,3715,3717,3719,3721,3723,3725,3728,3730,3733,3735,3737],{"class":1068,"line":9},[1066,3714,1335],{"class":1270},[1066,3716,2643],{"class":1075},[1066,3718,3679],{"class":1311},[1066,3720,2649],{"class":1458},[1066,3722,99],{"class":1145},[1066,3724,1470],{"class":1075},[1066,3726,3727],{"class":1091}," RemoveNode",[1066,3729,1102],{"class":1075},[1066,3731,3732],{"class":1311},"nodeID",[1066,3734,2665],{"class":1145},[1066,3736,1470],{"class":1075},[1066,3738,3112],{"class":1145},[981,3740,3741],{},"Removes a node from topology. Error if not found.",[2566,3743,718],{"id":3744},"topologygetnode",[1057,3746,3748],{"className":1059,"code":3747,"language":1061,"meta":144,"style":144},"func (t *Topology) GetNode(nodeID string) (NodeInfo, bool)\n",[1063,3749,3750],{"__ignoreMap":144},[1066,3751,3752,3754,3756,3758,3760,3762,3764,3767,3769,3771,3773,3775,3777,3779,3781,3783],{"class":1068,"line":9},[1066,3753,1335],{"class":1270},[1066,3755,2643],{"class":1075},[1066,3757,3679],{"class":1311},[1066,3759,2649],{"class":1458},[1066,3761,99],{"class":1145},[1066,3763,1470],{"class":1075},[1066,3765,3766],{"class":1091}," GetNode",[1066,3768,1102],{"class":1075},[1066,3770,3732],{"class":1311},[1066,3772,2665],{"class":1145},[1066,3774,1470],{"class":1075},[1066,3776,2643],{"class":1075},[1066,3778,839],{"class":1145},[1066,3780,1076],{"class":1075},[1066,3782,3270],{"class":1145},[1066,3784,1325],{"class":1075},[981,3786,3787],{},"Returns a node by ID.",[2566,3789,723],{"id":3790},"topologygetallnodes",[1057,3792,3794],{"className":1059,"code":3793,"language":1061,"meta":144,"style":144},"func (t *Topology) GetAllNodes() []NodeInfo\n",[1063,3795,3796],{"__ignoreMap":144},[1066,3797,3798,3800,3802,3804,3806,3808,3810,3813,3815,3817],{"class":1068,"line":9},[1066,3799,1335],{"class":1270},[1066,3801,2643],{"class":1075},[1066,3803,3679],{"class":1311},[1066,3805,2649],{"class":1458},[1066,3807,99],{"class":1145},[1066,3809,1470],{"class":1075},[1066,3811,3812],{"class":1091}," GetAllNodes",[1066,3814,1341],{"class":1075},[1066,3816,3305],{"class":1075},[1066,3818,3819],{"class":1145},"NodeInfo\n",[981,3821,3822],{},"Returns all nodes in topology.",[2566,3824,728],{"id":3825},"topologygetserviceproviders",[1057,3827,3829],{"className":1059,"code":3828,"language":1061,"meta":144,"style":144},"func (t *Topology) GetServiceProviders(name, version string) []NodeInfo\n",[1063,3830,3831],{"__ignoreMap":144},[1066,3832,3833,3835,3837,3839,3841,3843,3845,3848,3850,3852,3854,3857,3859,3861,3863],{"class":1068,"line":9},[1066,3834,1335],{"class":1270},[1066,3836,2643],{"class":1075},[1066,3838,3679],{"class":1311},[1066,3840,2649],{"class":1458},[1066,3842,99],{"class":1145},[1066,3844,1470],{"class":1075},[1066,3846,3847],{"class":1091}," GetServiceProviders",[1066,3849,1102],{"class":1075},[1066,3851,2704],{"class":1311},[1066,3853,1076],{"class":1075},[1066,3855,3856],{"class":1311}," version",[1066,3858,2665],{"class":1145},[1066,3860,1470],{"class":1075},[1066,3862,3305],{"class":1075},[1066,3864,3819],{"class":1145},[981,3866,3867],{},"Returns nodes providing the specified service and version.",[2566,3869,733],{"id":3870},"topologygetnodesbyservice",[1057,3872,3874],{"className":1059,"code":3873,"language":1061,"meta":144,"style":144},"func (t *Topology) GetNodesByService(name string) []NodeInfo\n",[1063,3875,3876],{"__ignoreMap":144},[1066,3877,3878,3880,3882,3884,3886,3888,3890,3893,3895,3897,3899,3901,3903],{"class":1068,"line":9},[1066,3879,1335],{"class":1270},[1066,3881,2643],{"class":1075},[1066,3883,3679],{"class":1311},[1066,3885,2649],{"class":1458},[1066,3887,99],{"class":1145},[1066,3889,1470],{"class":1075},[1066,3891,3892],{"class":1091}," GetNodesByService",[1066,3894,1102],{"class":1075},[1066,3896,2704],{"class":1311},[1066,3898,2665],{"class":1145},[1066,3900,1470],{"class":1075},[1066,3902,3305],{"class":1075},[1066,3904,3819],{"class":1145},[981,3906,3907],{},"Returns nodes providing any version of the specified service.",[2566,3909,738],{"id":3910},"topologygetversion",[1057,3912,3914],{"className":1059,"code":3913,"language":1061,"meta":144,"style":144},"func (t *Topology) GetVersion() int64\n",[1063,3915,3916],{"__ignoreMap":144},[1066,3917,3918,3920,3922,3924,3926,3928,3930,3933,3935],{"class":1068,"line":9},[1066,3919,1335],{"class":1270},[1066,3921,2643],{"class":1075},[1066,3923,3679],{"class":1311},[1066,3925,2649],{"class":1458},[1066,3927,99],{"class":1145},[1066,3929,1470],{"class":1075},[1066,3931,3932],{"class":1091}," GetVersion",[1066,3934,1341],{"class":1075},[1066,3936,3937],{"class":1145}," int64\n",[981,3939,3940],{},"Returns the topology version number.",[3078,3942],{},[1052,3944,109],{"id":3945},"serviceclientpool",[2566,3947,746],{"id":3948},"newserviceclientpool",[1057,3950,3952],{"className":1059,"code":3951,"language":1061,"meta":144,"style":144},"func NewServiceClientPool(node *Node) *ServiceClientPool\n",[1063,3953,3954],{"__ignoreMap":144},[1066,3955,3956,3958,3961,3963,3965,3967,3969,3971,3973],{"class":1068,"line":9},[1066,3957,1335],{"class":1270},[1066,3959,3960],{"class":1091}," NewServiceClientPool",[1066,3962,1102],{"class":1075},[1066,3964,1072],{"class":1311},[1066,3966,1459],{"class":1458},[1066,3968,89],{"class":1145},[1066,3970,1470],{"class":1075},[1066,3972,1459],{"class":1458},[1066,3974,3975],{"class":1145},"ServiceClientPool\n",[981,3977,3978],{},"Creates a connection pool for service clients.",[2566,3980,751],{"id":3981},"serviceclientpoolgetconn",[1057,3983,3985],{"className":1059,"code":3984,"language":1061,"meta":144,"style":144},"func (p *ServiceClientPool) GetConn(ctx context.Context, name, version string) (*grpc.ClientConn, error)\n",[1063,3986,3987],{"__ignoreMap":144},[1066,3988,3989,3991,3993,3996,3998,4000,4002,4005,4007,4009,4011,4013,4015,4017,4020,4022,4024,4026,4028,4030,4032,4034,4036,4039,4041,4043],{"class":1068,"line":9},[1066,3990,1335],{"class":1270},[1066,3992,2643],{"class":1075},[1066,3994,3995],{"class":1311},"p ",[1066,3997,2649],{"class":1458},[1066,3999,109],{"class":1145},[1066,4001,1470],{"class":1075},[1066,4003,4004],{"class":1091}," GetConn",[1066,4006,1102],{"class":1075},[1066,4008,1780],{"class":1311},[1066,4010,1748],{"class":1145},[1066,4012,1088],{"class":1075},[1066,4014,775],{"class":1145},[1066,4016,1076],{"class":1075},[1066,4018,4019],{"class":1311}," name",[1066,4021,1076],{"class":1075},[1066,4023,3856],{"class":1311},[1066,4025,2665],{"class":1145},[1066,4027,1470],{"class":1075},[1066,4029,2643],{"class":1075},[1066,4031,2649],{"class":1458},[1066,4033,1462],{"class":1145},[1066,4035,1088],{"class":1075},[1066,4037,4038],{"class":1145},"ClientConn",[1066,4040,1076],{"class":1075},[1066,4042,3071],{"class":1145},[1066,4044,1325],{"class":1075},[981,4046,4047],{},"Returns a connection to a service provider. Uses round-robin across providers.",[981,4049,4050],{},[2032,4051,4052],{},"Errors:",[2026,4054,4055,4061],{},[2029,4056,4057,4060],{},[1063,4058,4059],{},"ErrNoProviders"," — No nodes provide this service",[2029,4062,4063,4066],{},[1063,4064,4065],{},"ErrNoTLSConfig"," — Node has no TLS configuration",[2566,4068,756],{"id":4069},"serviceclientpoolclose",[1057,4071,4073],{"className":1059,"code":4072,"language":1061,"meta":144,"style":144},"func (p *ServiceClientPool) Close() error\n",[1063,4074,4075],{"__ignoreMap":144},[1066,4076,4077,4079,4081,4083,4085,4087,4089,4092,4094],{"class":1068,"line":9},[1066,4078,1335],{"class":1270},[1066,4080,2643],{"class":1075},[1066,4082,3995],{"class":1311},[1066,4084,2649],{"class":1458},[1066,4086,109],{"class":1145},[1066,4088,1470],{"class":1075},[1066,4090,4091],{"class":1091}," Close",[1066,4093,1341],{"class":1075},[1066,4095,3112],{"class":1145},[981,4097,4098],{},"Closes all connections in the pool.",[3078,4100],{},[1052,4102,761],{"id":4103},"serviceclient",[2566,4105,765],{"id":4106},"newserviceclient",[1057,4108,4110],{"className":1059,"code":4109,"language":1061,"meta":144,"style":144},"func NewServiceClient[T any](pool *ServiceClientPool, name, version string, newClient func(grpc.ClientConnInterface) T) *ServiceClient[T]\n",[1063,4111,4112],{"__ignoreMap":144},[1066,4113,4114,4116,4119,4122,4125,4128,4131,4133,4135,4137,4139,4141,4143,4145,4147,4149,4152,4155,4157,4159,4161,4164,4166,4169,4171,4173,4175,4177,4179],{"class":1068,"line":9},[1066,4115,1335],{"class":1270},[1066,4117,4118],{"class":1091}," NewServiceClient",[1066,4120,4121],{"class":1075},"[",[1066,4123,4124],{"class":1311},"T",[1066,4126,4127],{"class":1145}," any",[1066,4129,4130],{"class":1075},"](",[1066,4132,1706],{"class":1311},[1066,4134,1459],{"class":1458},[1066,4136,109],{"class":1145},[1066,4138,1076],{"class":1075},[1066,4140,4019],{"class":1311},[1066,4142,1076],{"class":1075},[1066,4144,3856],{"class":1311},[1066,4146,2665],{"class":1145},[1066,4148,1076],{"class":1075},[1066,4150,4151],{"class":1311}," newClient",[1066,4153,4154],{"class":1270}," func",[1066,4156,1102],{"class":1075},[1066,4158,1462],{"class":1145},[1066,4160,1088],{"class":1075},[1066,4162,4163],{"class":1145},"ClientConnInterface",[1066,4165,1470],{"class":1075},[1066,4167,4168],{"class":1145}," T",[1066,4170,1470],{"class":1075},[1066,4172,1459],{"class":1458},[1066,4174,761],{"class":1145},[1066,4176,4121],{"class":1075},[1066,4178,4124],{"class":1145},[1066,4180,4181],{"class":1075},"]\n",[981,4183,4184],{},"Creates a typed service client.",[981,4186,4187],{},[2032,4188,2608],{},[1057,4190,4192],{"className":1059,"code":4191,"language":1061,"meta":144,"style":144},"client := aegis.NewServiceClient(pool, \"identity\", \"v1\", identity.NewIdentityServiceClient)\n",[1063,4193,4194],{"__ignoreMap":144},[1066,4195,4196,4199,4201,4203,4205,4207,4209,4211,4213,4215,4217,4219,4221,4223,4225,4227],{"class":1068,"line":9},[1066,4197,4198],{"class":1071},"client",[1066,4200,1082],{"class":1071},[1066,4202,1085],{"class":1071},[1066,4204,1088],{"class":1075},[1066,4206,765],{"class":1091},[1066,4208,1102],{"class":1075},[1066,4210,1706],{"class":1071},[1066,4212,1076],{"class":1075},[1066,4214,1162],{"class":1105},[1066,4216,1076],{"class":1075},[1066,4218,1172],{"class":1105},[1066,4220,1076],{"class":1075},[1066,4222,1719],{"class":1071},[1066,4224,1088],{"class":1075},[1066,4226,1724],{"class":1071},[1066,4228,1325],{"class":1075},[2566,4230,770],{"id":4231},"serviceclientget",[1057,4233,4235],{"className":1059,"code":4234,"language":1061,"meta":144,"style":144},"func (sc *ServiceClient[T]) Get(ctx context.Context) (T, error)\n",[1063,4236,4237],{"__ignoreMap":144},[1066,4238,4239,4241,4243,4246,4248,4250,4252,4254,4257,4260,4262,4264,4266,4268,4270,4272,4274,4276,4278,4280],{"class":1068,"line":9},[1066,4240,1335],{"class":1270},[1066,4242,2643],{"class":1075},[1066,4244,4245],{"class":1311},"sc ",[1066,4247,2649],{"class":1458},[1066,4249,761],{"class":1145},[1066,4251,4121],{"class":1075},[1066,4253,4124],{"class":1145},[1066,4255,4256],{"class":1075},"])",[1066,4258,4259],{"class":1091}," Get",[1066,4261,1102],{"class":1075},[1066,4263,1780],{"class":1311},[1066,4265,1748],{"class":1145},[1066,4267,1088],{"class":1075},[1066,4269,775],{"class":1145},[1066,4271,1470],{"class":1075},[1066,4273,2643],{"class":1075},[1066,4275,4124],{"class":1145},[1066,4277,1076],{"class":1075},[1066,4279,3071],{"class":1145},[1066,4281,1325],{"class":1075},[981,4283,4284],{},"Returns a client connected to a provider.",[3078,4286],{},[1052,4288,775],{"id":4289},"context",[2566,4291,779],{"id":4292},"callerfromcontext",[1057,4294,4296],{"className":1059,"code":4295,"language":1061,"meta":144,"style":144},"func CallerFromContext(ctx context.Context) (*Caller, error)\n",[1063,4297,4298],{"__ignoreMap":144},[1066,4299,4300,4302,4305,4307,4309,4311,4313,4315,4317,4319,4321,4323,4325,4327],{"class":1068,"line":9},[1066,4301,1335],{"class":1270},[1066,4303,4304],{"class":1091}," CallerFromContext",[1066,4306,1102],{"class":1075},[1066,4308,1780],{"class":1311},[1066,4310,1748],{"class":1145},[1066,4312,1088],{"class":1075},[1066,4314,775],{"class":1145},[1066,4316,1470],{"class":1075},[1066,4318,2643],{"class":1075},[1066,4320,2649],{"class":1458},[1066,4322,114],{"class":1145},[1066,4324,1076],{"class":1075},[1066,4326,3071],{"class":1145},[1066,4328,1325],{"class":1075},[981,4330,4331],{},"Extracts caller identity from gRPC context.",[981,4333,4334],{},[2032,4335,4052],{},[2026,4337,4338,4344,4350],{},[2029,4339,4340,4343],{},[1063,4341,4342],{},"ErrNoPeerInfo"," — No peer info in context",[2029,4345,4346,4349],{},[1063,4347,4348],{},"ErrNoTLSInfo"," — Peer has no TLS info",[2029,4351,4352,4355],{},[1063,4353,4354],{},"ErrNoCertificate"," — No client certificate",[2566,4357,784],{"id":4358},"mustcallerfromcontext",[1057,4360,4362],{"className":1059,"code":4361,"language":1061,"meta":144,"style":144},"func MustCallerFromContext(ctx context.Context) *Caller\n",[1063,4363,4364],{"__ignoreMap":144},[1066,4365,4366,4368,4371,4373,4375,4377,4379,4381,4383,4385],{"class":1068,"line":9},[1066,4367,1335],{"class":1270},[1066,4369,4370],{"class":1091}," MustCallerFromContext",[1066,4372,1102],{"class":1075},[1066,4374,1780],{"class":1311},[1066,4376,1748],{"class":1145},[1066,4378,1088],{"class":1075},[1066,4380,775],{"class":1145},[1066,4382,1470],{"class":1075},[1066,4384,1459],{"class":1458},[1066,4386,4387],{"class":1145},"Caller\n",[981,4389,4390],{},"Extracts caller identity, panics on error. Use only when mTLS is guaranteed.",[3078,4392],{},[1052,4394,789],{"id":4395},"health",[2566,4397,793],{"id":4398},"newhealthinfo",[1057,4400,4402],{"className":1059,"code":4401,"language":1061,"meta":144,"style":144},"func NewHealthInfo() *HealthInfo\n",[1063,4403,4404],{"__ignoreMap":144},[1066,4405,4406,4408,4411,4413,4415],{"class":1068,"line":9},[1066,4407,1335],{"class":1270},[1066,4409,4410],{"class":1091}," NewHealthInfo",[1066,4412,1341],{"class":1075},[1066,4414,1459],{"class":1458},[1066,4416,4417],{"class":1145},"HealthInfo\n",[981,4419,4420],{},"Creates health info with unknown status.",[2566,4422,798],{"id":4423},"newfunctionhealthchecker",[1057,4425,4427],{"className":1059,"code":4426,"language":1061,"meta":144,"style":144},"func NewFunctionHealthChecker(name string, fn func(context.Context) error) *FunctionHealthChecker\n",[1063,4428,4429],{"__ignoreMap":144},[1066,4430,4431,4433,4436,4438,4440,4442,4444,4447,4449,4451,4453,4455,4457,4459,4461,4463,4465],{"class":1068,"line":9},[1066,4432,1335],{"class":1270},[1066,4434,4435],{"class":1091}," NewFunctionHealthChecker",[1066,4437,1102],{"class":1075},[1066,4439,2704],{"class":1311},[1066,4441,2665],{"class":1145},[1066,4443,1076],{"class":1075},[1066,4445,4446],{"class":1311}," fn",[1066,4448,4154],{"class":1270},[1066,4450,1102],{"class":1075},[1066,4452,4289],{"class":1145},[1066,4454,1088],{"class":1075},[1066,4456,775],{"class":1145},[1066,4458,1470],{"class":1075},[1066,4460,3071],{"class":1145},[1066,4462,1470],{"class":1075},[1066,4464,1459],{"class":1458},[1066,4466,4467],{"class":1145},"FunctionHealthChecker\n",[981,4469,4470],{},"Creates a health checker from a function.",[3078,4472],{},[1052,4474,803],{"id":4475},"tls",[2566,4477,807],{"id":4478},"loadorgeneratetls",[1057,4480,4482],{"className":1059,"code":4481,"language":1061,"meta":144,"style":144},"func LoadOrGenerateTLS(nodeID, certDir string) (*TLSConfig, error)\n",[1063,4483,4484],{"__ignoreMap":144},[1066,4485,4486,4488,4491,4493,4495,4497,4500,4502,4504,4506,4508,4510,4512,4514],{"class":1068,"line":9},[1066,4487,1335],{"class":1270},[1066,4489,4490],{"class":1091}," LoadOrGenerateTLS",[1066,4492,1102],{"class":1075},[1066,4494,3732],{"class":1311},[1066,4496,1076],{"class":1075},[1066,4498,4499],{"class":1311}," certDir",[1066,4501,2665],{"class":1145},[1066,4503,1470],{"class":1075},[1066,4505,2643],{"class":1075},[1066,4507,2649],{"class":1458},[1066,4509,891],{"class":1145},[1066,4511,1076],{"class":1075},[1066,4513,3071],{"class":1145},[1066,4515,1325],{"class":1075},[981,4517,4518],{},"Loads certificates from directory, generating if missing.",[2566,4520,812],{"id":4521},"loadtlsconfig",[1057,4523,4525],{"className":1059,"code":4524,"language":1061,"meta":144,"style":144},"func LoadTLSConfig(opts *TLSOptions) (*TLSConfig, error)\n",[1063,4526,4527],{"__ignoreMap":144},[1066,4528,4529,4531,4534,4536,4538,4540,4542,4544,4546,4548,4550,4552,4554],{"class":1068,"line":9},[1066,4530,1335],{"class":1270},[1066,4532,4533],{"class":1091}," LoadTLSConfig",[1066,4535,1102],{"class":1075},[1066,4537,3017],{"class":1311},[1066,4539,1459],{"class":1458},[1066,4541,881],{"class":1145},[1066,4543,1470],{"class":1075},[1066,4545,2643],{"class":1075},[1066,4547,2649],{"class":1458},[1066,4549,891],{"class":1145},[1066,4551,1076],{"class":1075},[1066,4553,3071],{"class":1145},[1066,4555,1325],{"class":1075},[981,4557,4558],{},"Loads TLS configuration from options.",[3078,4560],{},[1052,4562,32],{"id":4563},"next-steps",[2026,4565,4566,4571],{},[2029,4567,4568,2217],{},[984,4569,821],{"href":4570},"types",[2029,4572,4573,4576],{},[984,4574,80],{"href":4575},"../learn/concepts"," — Mental models",[2246,4578,4579],{},"html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}",{"title":144,"searchDepth":19,"depth":19,"links":4581},[4582,4594,4609,4618,4623,4627,4631,4635,4639],{"id":2564,"depth":19,"text":583,"children":4583},[4584,4585,4586,4587,4588,4589,4590,4591,4592,4593],{"id":2568,"depth":150,"text":587},{"id":2631,"depth":150,"text":592},{"id":2677,"depth":150,"text":597},{"id":2718,"depth":150,"text":602},{"id":2762,"depth":150,"text":607},{"id":2803,"depth":150,"text":612},{"id":2847,"depth":150,"text":617},{"id":2949,"depth":150,"text":622},{"id":2990,"depth":150,"text":627},{"id":3036,"depth":150,"text":632},{"id":1072,"depth":19,"text":89,"children":4595},[4596,4597,4598,4599,4600,4601,4602,4603,4604,4605,4606,4607,4608],{"id":3084,"depth":150,"text":640},{"id":3118,"depth":150,"text":645},{"id":3150,"depth":150,"text":650},{"id":3190,"depth":150,"text":655},{"id":3229,"depth":150,"text":660},{"id":3278,"depth":150,"text":665},{"id":3316,"depth":150,"text":670},{"id":3376,"depth":150,"text":675},{"id":3435,"depth":150,"text":680},{"id":3483,"depth":150,"text":685},{"id":3525,"depth":150,"text":690},{"id":3577,"depth":150,"text":695},{"id":3627,"depth":150,"text":700},{"id":3662,"depth":19,"text":99,"children":4610},[4611,4612,4613,4614,4615,4616,4617],{"id":3665,"depth":150,"text":708},{"id":3705,"depth":150,"text":713},{"id":3744,"depth":150,"text":718},{"id":3790,"depth":150,"text":723},{"id":3825,"depth":150,"text":728},{"id":3870,"depth":150,"text":733},{"id":3910,"depth":150,"text":738},{"id":3945,"depth":19,"text":109,"children":4619},[4620,4621,4622],{"id":3948,"depth":150,"text":746},{"id":3981,"depth":150,"text":751},{"id":4069,"depth":150,"text":756},{"id":4103,"depth":19,"text":761,"children":4624},[4625,4626],{"id":4106,"depth":150,"text":765},{"id":4231,"depth":150,"text":770},{"id":4289,"depth":19,"text":775,"children":4628},[4629,4630],{"id":4292,"depth":150,"text":779},{"id":4358,"depth":150,"text":784},{"id":4395,"depth":19,"text":789,"children":4632},[4633,4634],{"id":4398,"depth":150,"text":793},{"id":4423,"depth":150,"text":798},{"id":4475,"depth":19,"text":803,"children":4636},[4637,4638],{"id":4478,"depth":150,"text":807},{"id":4521,"depth":150,"text":812},{"id":4563,"depth":19,"text":32},{},"2025-02-18T00:00:00.000Z",null,{"title":574,"description":576},[979,4645,4646],"reference","api","9vo_i_8UerMPGdiF2ni1wLElx_Q6xm578LWOgRXm4is",[4649,4650],{"title":475,"path":474,"stem":940,"description":477,"children":-1},{"title":821,"path":820,"stem":949,"description":823,"children":-1},1776267328613]