devedge-sdk

module
v0.28.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 28, 2026 License: Apache-2.0

README ΒΆ

devedge-sdk

CI Docs Go Reference

A complete, secure-by-default Infoblox microservice from a single proto. Define a resource and its methods in proto; devedge-sdk gives you a running gRPC + REST service with Google-AIP semantics, fail-closed authorization, multi-tenant isolation, and a persistence/eventing layer wired in β€” every backend a swappable seam, every security invariant provable in CI.

It is the runtime companion to devedge: devedge is the dev- and deploy-time edge; devedge-sdk is the runtime library that production services import.

Status: early. APIs will change. Pin a version and read the changelog/releases before upgrading. The operational baseline is still filling in β€” built-in observability and health/readiness probes are tracked on the foundation roadmap (#97).

πŸ“š Documentation

Full docs live at infobloxopen.github.io/devedge-sdk. Start here:

Section What's there
πŸš€ Getting Started Install the SDK and stand up a running, fail-closed service in five minutes (Quickstart).
πŸ’‘ Concepts The architecture and seam model, the annotation contract, tenant isolation, aggregates, and events.
πŸ“– Guides Task how-tos: define a service, model a resource, pick a storage shape, run seccheck.
πŸ“‘ Reference Per-package API reference and codegen-plugin docs.
πŸŽ“ Tutorial Build the API Key Manager service end to end.

Why devedge-sdk

  • From one proto to a running, AIP-correct service. Scaffold a service, declare your resource and methods, and server.New stands up gRPC plus an optional HTTP/JSON gateway with the standard methods and Google-AIP semantics already wired: field-mask PATCH (AIP-134), ETag/If-Matchβ†’412 optimistic concurrency (AIP-154), pagination, filtering (AIP-160), soft-delete + undelete (AIP-148/149), batch methods (AIP-137), request de-duplication (AIP-155), and long-running operations (AIP-151/152). Correct API semantics, for free.
  • Secure by default β€” and provable. Authorization is fail-closed: annotate each RPC with (infoblox.authz.v1.rule) and the service refuses to boot if any served method is undeclared. Every query is scoped by account-id at the storage layer (GORM and ent), so one principal can never see another's resources; secret-annotated fields are encrypted at rest and never returned; and errors never leak SQL or stack traces. The seccheck package proves all of it in CI.
  • Pluggable seams, dev defaults, zero service-code change. Persistence (in-memory β†’ GORM/ent), transactions, domain events (in-memory β†’ Kafka, via a transactional outbox), the authz decision point, and the secret encryptor each ship a dev-suitable default and swap for a production backend without touching service code. DDD aggregates and multi-surface projections are first-class.
  • Codegen from your proto; dependency-light core. protoc-gen-svc scaffolds the service, protoc-gen-storage emits a GORM repository, protoc-gen-ent emits an ent schema, and protoc-gen-devedge-authz emits the authz-rules table β€” the proto is the single source of truth. Core packages depend only on the standard library: no ORM, no policy-engine dependency.

Install

go get github.com/infobloxopen/devedge-sdk@latest

Install the codegen plugins onto your PATH so buf generate can invoke them:

go install github.com/infobloxopen/devedge-sdk/cmd/protoc-gen-svc@latest
go install github.com/infobloxopen/devedge-sdk/cmd/protoc-gen-storage@latest
go install github.com/infobloxopen/devedge-sdk/cmd/protoc-gen-ent@latest
go install github.com/infobloxopen/devedge-sdk/cmd/protoc-gen-devedge-authz@latest

Requires Go 1.25+ and buf. Postgres and Vault are only needed for the production storage and secret backends β€” the in-memory store and dev encryptor run in-process. Full prerequisites: Installation.

Quickstart

In about five minutes you go from a .proto to a running, fail-closed gRPC + REST service.

1. Declare each RPC's authz requirement in proto β€” verb + resource is all a method needs:

service WidgetService {
  rpc GetWidget(GetWidgetRequest) returns (Widget) {
    option (infoblox.authz.v1.rule) = {verb: "get", resource: "widget:{id}"};
  }
  rpc CreateWidget(CreateWidgetRequest) returns (Widget) {
    option (infoblox.authz.v1.rule) = {verb: "create", resource: "widget"};
  }
}

2. Generate (buf generate) β€” protoc-gen-devedge-authz emits a WidgetServiceAuthzRules table next to the .pb.go.

3. Wire the server. The Authorizer defaults to default-deny, so every call is denied until you grant something β€” fail-closed by construction:

srv, err := server.New(server.Config{
    GRPCAddr: ":9090",
    HTTPAddr: ":8080", // optional HTTP/JSON gateway; omit to run gRPC-only
    Rules:    widgetv1.WidgetServiceAuthzRules, // generated in step 2

    // Dev decision point β€” grant group:admin everything. Swap for an
    // OPA/Cedar/remote Authorizer in production; nothing else changes.
    Authorizer: authz.NewDevAuthorizer(authz.Grant{
        Tenant: "t1", Subjects: []string{"group:admin"},
        Verbs: []authz.Verb{"*"}, Resource: "*",
    }),
    // Derive the principal from request metadata (account-id β†’ tenant, groups β†’
    // group:<name>). Use a verified-token func in production.
    PrincipalFunc: grpcauthz.DevPrincipalFunc(),
})
if err != nil {
    log.Fatal(err)
}

widgetv1.RegisterWidgetServiceServer(srv.GRPCServer(), &widgetServer{})
log.Fatal(srv.Serve(ctx)) // blocks until ctx is cancelled

You now have a running, fail-closed service speaking gRPC and REST. The chain server.New builds, outermost first:

RequestID β†’ ErrorMapper β†’ TenantID β†’ grpcauthz (fail-closed) β†’ FieldMask β†’ ETag/412 β†’ ReadMask β†’ ValidateOnly β†’ Deduplicate

β†’ Full walkthrough with tests: Quickstart.

Packages

Package What it provides
server Server lifecycle: gRPC + optional HTTP/JSON gateway, the interceptor chain auto-wired, graceful shutdown.
middleware The interceptors: RequestID, TenantID, FieldMask, ErrorMapper, ValidateOnly, Dedup, and etag.
authz Engine-neutral model: Principal, Resource, Verb, AccessRequest, Decision, the pluggable Authorizer, and DevAuthorizer (in-process, default-deny).
authz/grpcauthz Fail-closed gRPC interceptor + boot gate. Rough-compatible with atlas-authz-middleware/grpc_opa (COMPAT.md).
authz/catalog Builds the permission catalog (per resource: verbs, endpoints, View/Manage groups) from declared rules.
authz/authzpb Reflection-based rule extractor β€” reads (infoblox.authz.v1.rule) off linked descriptors, no generated file.
persistence ORM-free Repository[T,K] seam, in-memory dev store, transactions, batch, DSN hotload, filtering, and aggregate roots. Storage shape is per-service (SHAPES.md).
events Domain events via a transactional outbox β€” Publisher/Bus/idempotency, an in-memory bus, and a Kafka bus, for safe cross-aggregate reactions.
secret Secret-at-rest Encryptor: AES-256-GCM + HMAC for dev, HashiCorp Vault Transit for prod.
lro AIP-151/152 long-running operations: Store, Manager, Operation, cancellation.
seccheck Static + dynamic security assertions you run in CI (see Security model).

Full API reference for every package: Reference docs β†—.

Codegen plugins

The proto is the single source of truth; make generate (or buf generate) drives these:

Plugin (cmd/…) Output
protoc-gen-svc service scaffold (*.svc.go)
protoc-gen-storage GORM-backed Repository (*.storage.go)
protoc-gen-ent ent schema (ent/schema/*.go)
protoc-gen-devedge-authz the <Service>AuthzRules []MethodRule table (*.authz.go)

Security model

Security is a foundation property here, not a bolt-on: it is enforced by construction and verified in CI, so a correctly-built service is secure by default.

Authorization is fail-closed: an undeclared or ungranted method is denied with no code required. The seccheck package turns the model's invariants into assertions you run in CI (make security-check):

Invariant Asserted by
Every RPC has (infoblox.authz.v1.rule) or public: true AssertRulesComplete
No secret-annotated field value in a read/list response AssertNoSecretFieldsLeaked
Unknown principal β†’ PermissionDenied on all RPCs AssertUnknownPrincipalDenied
Account A cannot read Account B's resources AssertCrossAccountIsolation
Errors never leak SQL, stack traces, or hostnames AssertErrorMessagesClean

β†’ Security Check guide.

Secret fields. Mark a field secret in proto; generated code hashes it for lookup and encrypts the ciphertext β€” AES-256-GCM in dev, HashiCorp Vault Transit in production β€” so plaintext is never persisted and never returned. See model a resource β†’ secret fields and the Vault Transit guide.

Swapping the decision point

WithAuthorizer / Config.Authorizer takes any authz.Authorizer. To target a production engine, implement the one-method interface β€” an OPA-backed authorizer calling a sidecar, a Cedar/OpenFGA client, a remote PDP β€” and pass it in. Nothing else in the service changes. The SDK core stays engine-neutral: no OPA, no ORM, no policy-model types β€” those belong in adapters built on the SDK.

Building from source

This repo is a multi-module workspace (WS-011 / F039): the dep-light root library plus six nested modules (cmd, config/koanf, events/kafkabus, observability/otel, persistence/gormtx, persistence/entrepo). A committed go.work resolves the cross-module references locally; the build/vet/test targets loop over every module (the MODULES list in the Makefile).

make build                 # build every module (via go.work)
make test                  # unit tests across every module
make vet                   # go vet across every module
make lint                  # golangci-lint if installed, else go vet
make build-gowork-off      # build each module with the workspace OFF (real requires only)
make check-graph-isolation # prove a server-only consumer's graph is free of the heavy adapter deps
make generate              # rebuild plugins + regenerate after any .proto change
make security-check        # run the seccheck assertions
make release VERSION=vX.Y.Z   # synchronized multi-module release (dry run; PUSH=1 to publish)

testdata/toy, testdata/apikey, testdata/fleet, and testdata/iam are separate consumer modules with their own integration tests, deliberately NOT in go.work β€” run them with cd testdata/<name> && GOWORK=off go test ./.... To carve a future heavy component into its own isolated module, follow the Adding an Isolated Module checklist. See AGENTS.md for the full contributor guide.

License

Apache-2.0. See LICENSE.

Directories ΒΆ

Path Synopsis
authn
oidc module
Package authz defines a clean, transport-neutral, pluggable authorization model for Infoblox services.
Package authz defines a clean, transport-neutral, pluggable authorization model for Infoblox services.
authzpb
Package authzpb extracts declared authorization rules from compiled protobuf descriptors.
Package authzpb extracts declared authorization rules from compiled protobuf descriptors.
catalog
Package catalog turns declared method rules into the permission catalog β€” the code-backed source of truth that the API enforces, that a portal can render as a role-creation UI, and that a downstream engine/policy generator can consume.
Package catalog turns declared method rules into the permission catalog β€” the code-backed source of truth that the API enforces, that a portal can render as a role-creation UI, and that a downstream engine/policy generator can consume.
grpcauthz
Package grpcauthz wires the SDK's pluggable authz.Authorizer into gRPC as a fail-closed server interceptor.
Package grpcauthz wires the SDK's pluggable authz.Authorizer into gRPC as a fail-closed server interceptor.
cmd module
Package config provides a stdlib-only, dependency-light configuration seam.
Package config provides a stdlib-only, dependency-light configuration seam.
koanf module
Package events is the F032 transactional-outbox + domain-events seam: a backend-neutral way to react to a change in one aggregate by changing another, WITHOUT making the two changes one transaction (which F031 forbids across aggregate boundaries) and WITHOUT a dual write that loses the second step on a crash.
Package events is the F032 transactional-outbox + domain-events seam: a backend-neutral way to react to a change in one aggregate by changing another, WITHOUT making the two changes one transaction (which F031 forbids across aggregate boundaries) and WITHOUT a dual write that loses the second step on a crash.
membus
Package membus is the in-process, channel-based dev/embedded implementation of the events.Bus seam β€” Phase 1 of the SDK event-bus stack.
Package membus is the in-process, channel-based dev/embedded implementation of the events.Bus seam β€” Phase 1 of the SDK event-bus stack.
kafkabus module
Package health provides the readiness-check seam for devedge-sdk services.
Package health provides the readiness-check seam for devedge-sdk services.
internal
Package lro implements the AIP-151 Long-Running Operation pattern.
Package lro implements the AIP-151 Long-Running Operation pattern.
etag
Package etag provides gRPC middleware for HTTP ETag / conditional-request semantics: it reads the If-Match precondition from incoming metadata and writes the ETag for the response to the outgoing trailer.
Package etag provides gRPC middleware for HTTP ETag / conditional-request semantics: it reads the If-Match precondition from incoming metadata and writes the ETag for the response to the outgoing trailer.
redact
Package redact provides proto-reflection-based helpers that replace (infoblox.field.v1.opts).secret = true field values with "[REDACTED]" before logging.
Package redact provides proto-reflection-based helpers that replace (infoblox.field.v1.opts).secret = true field values with "[REDACTED]" before logging.
observability
otel module
Package persistence provides connection and storage helpers for Infoblox services.
Package persistence provides connection and storage helpers for Infoblox services.
filter
Package filter implements an AIP-160 subset parser for list filter expressions and an AIP-132 parser for order_by strings.
Package filter implements an AIP-160 subset parser for list filter expressions and an AIP-132 parser for order_by strings.
resourcename
Package resourcename provides AIP-122 resource name formatting and parsing.
Package resourcename provides AIP-122 resource name formatting and parsing.
entrepo module
gormtx module
migrate module
proto
Package resilience provides policy interceptors for request timeouts, rate limiting, and circuit-breaker seams.
Package resilience provides policy interceptors for request timeouts, rate limiting, and circuit-breaker seams.
Package server provides a batteries-included gRPC server builder for Infoblox services.
Package server provides a batteries-included gRPC server builder for Infoblox services.
Package servicekit defines the composable-service contract for devedge-sdk: a service is split into an importable Module (owns DOMAIN behavior) and one or more executable HOSTS (own PROCESS behavior).
Package servicekit defines the composable-service contract for devedge-sdk: a service is split into an importable Module (owns DOMAIN behavior) and one or more executable HOSTS (own PROCESS behavior).
Package servicekittest provides generated-quality contract harnesses for the WS-012 composable-services test pyramid (proposal Β§7).
Package servicekittest provides generated-quality contract harnesses for the WS-012 composable-services test pyramid (proposal Β§7).
Package types provides reusable, ORM-agnostic field types for Infoblox resources.
Package types provides reusable, ORM-agnostic field types for Infoblox resources.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL