valiss

command module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jul 9, 2026 License: MIT Imports: 14 Imported by: 0

README

valiss

VALidator-ISSuer: tenant authentication for gRPC and HTTP services, modeled on NATS operator/account/user credentials.

  • An operator holds an Ed25519 nkey; its public key is the trust anchor.
  • The operator signs each account (tenant) a scoped, time-limited JWT that binds the account's own nkey public key. Issued token ids go in a server-side allowlist.
  • An account may delegate: it signs user tokens with its account seed, granting end users a subset of its scopes. Servers verify the chain up to the pinned operator key; nothing else needs distribution.
  • The client signs every request with its nkey over a timestamp. The server verifies the token (chain) against the operator key, the signature against the bound key within a skew window, and the account token id against the allowlist, then hands the tenant (and user) identity to the handler for data segmentation.

Key types map to nkeys directly: operator SO.../O..., account SA.../A..., user SU.../U....

Per-method authorization: grant call:<fullMethod> scopes (prefix wildcards like call:/pkg.Service/* and call:* supported) and enable WithMethodScope() on the authenticator. User scopes are clamped to the account's grants at verification, so a tenant can never delegate more than it holds.

Bearer credentials: a token whose scopes include bearer authenticates without a per-request signature (token-only, replayable). Meant for user entries marked bearer: true, where handing out a seed is impractical; pair with TLS and short TTLs.

Layout

main.go at the root is the CLI; the consumable library lives under pkg/.

  • pkg/token — token issue/verify (account and user level), request sign/verify, allowlist, the credential Verifier, and TenantFromContext
  • pkg/creds — client creds file (tokens + seed)
  • pkg/grpcauth — gRPC server interceptors and client per-RPC credentials
  • pkg/httpauth — net/http server middleware and client transport
  • internal/manifest — the valiss.yaml token manifest (CLI-only)
  • examples/ — runnable end-to-end demos for both transports

Library

Server (gRPC):

verifier := token.NewVerifier(operatorPubKey, allowlist)
auth := grpcauth.NewAuthenticator(verifier, grpcauth.WithMethodScope())
srv := grpc.NewServer(
    grpc.UnaryInterceptor(auth.UnaryInterceptor()),
    grpc.StreamInterceptor(auth.StreamInterceptor()),
)
// in a handler:
claims, _ := token.TenantFromContext(ctx) // claims.TenantID segments data,
                                          // claims.UserID names the end user

Client (gRPC):

c, _ := creds.Load("alice.creds")
rpcCreds, _ := grpcauth.NewCredentials(c)
conn, _ := grpc.NewClient(addr, grpc.WithPerRPCCredentials(rpcCreds), ...)

Server (HTTP):

mw := httpauth.NewMiddleware(token.NewVerifier(operatorPubKey, allowlist))
srv := &http.Server{Handler: mw(mux)}

Client (HTTP):

c, _ := creds.Load("alice.creds")
transport, _ := httpauth.NewTransport(c, nil)
client := &http.Client{Transport: transport}

Runnable versions: go run ./examples/grpcauth, go run ./examples/httpauth.

CLI

Stateless: key pairs are printed once at generation and never stored; signing seeds are supplied via VALISS_SEED_<PUBKEY> environment variables (a secrets manager that injects env fits naturally).

valiss keygen operator     # one-time: public key = server trust anchor
valiss keygen account      # per-tenant: public key goes in valiss.yaml
valiss keygen user         # per-end-user: public key goes in a user entry
valiss creds ACCOUNT[/USER]  # mint credentials for one entity

creds reads the token manifest (valiss.yaml in the working directory, override with -f FILE), resolves the required seeds from the environment (failing with the exact variable name when one is missing), and writes the credentials to stdout and their metadata to stderr:

$ export VALISS_SEED_OD25ZJ...=SOAI2X...   # operator seed
$ valiss creds acme > acme.creds
account:
  id: acme
  key: AC4JQU...
  jti: FVIENQPFQY...        # add to the server allowlist
  expires: 2026-08-08T09:00:00Z

Manifest entries without a key get a fresh pair generated per invocation; the seed ships only inside the creds.

User creds carry only the user token and seed (NATS-resolver style): the operator seed is not needed at mint time, so an account holder can issue its users on its own. The server resolves account tokens itself, e.g. from static configuration:

$ export VALISS_SEED_AC4JQU...=SAAK7G...   # account seed
$ valiss creds acme/alice > alice.creds
user:
  id: alice
  key: UDKED...
  jti: NQLQXOWTGN...
  expires: 2026-07-09T13:00:00Z
resolver, _ := token.StaticAccountTokens(acctTok1, acctTok2)
verifier := token.NewVerifier(operatorPubKey, allowlist,
    token.WithAccountTokenResolver(resolver))

Pass -bundle to mint a bundle instead: user creds that also embed a freshly minted account token (requiring the operator seed), verifiable by any server without a resolver. Each bundle mint yields a new account jti for the allowlist.

An annotated template ships as valiss.example.yaml:

# valiss.yaml — public data only, safe to commit
operator: ODZ6U...          # trust anchor; seed from VALISS_SEED_<this key>
accounts:
  - id: acme
    key: ABPZ7...            # account public key from `valiss keygen account`
    scopes: ["call:/pkg.Svc/*"]
    ttl: 720h                # optional, defaults to 720h
    users:
      - id: alice
        key: UDGH3...        # user public key; seed stays with the user
        scopes: ["call:/pkg.Svc/Get"]
        ttl: 1h              # optional, defaults to 1h
      - id: carol
        bearer: true         # keyless token-only credential
        scopes: ["call:/pkg.Svc/List"]

Documentation

Overview

Command valiss issues tenant credentials for gRPC and HTTP services using the operator/account/user nkey model (NATS style). It is stateless: key pairs are printed once at generation and never stored; signing seeds are supplied via VALISS_SEED_<PUBKEY> environment variables.

valiss keygen operator            # one-time: operator key pair (trust anchor)
valiss keygen account             # per-tenant key pair
valiss keygen user                # per-end-user key pair
valiss creds ACCOUNT[/USER]       # mint credentials for one entity

creds reads a token manifest (valiss.yaml in the working directory, override with -f) declaring the credential tree, resolves every required seed from VALISS_SEED_<PUBKEY> environment variables (failing when one is missing), and writes the credentials to stdout and their metadata -- including the jti the server-side allowlist accepts -- to stderr as YAML. User creds carry only the user token; -bundle additionally embeds a fresh account token for servers that do not resolve account tokens themselves. Manifest entries without a key get a fresh key pair generated per invocation; the seed ships inside the creds and is never stored.

Directories

Path Synopsis
examples
grpcauth command
Example grpcauth shows the full tenant-auth wiring for gRPC: an operator issues a scoped account token, the server installs the auth interceptors, and the client attaches the credential to every call.
Example grpcauth shows the full tenant-auth wiring for gRPC: an operator issues a scoped account token, the server installs the auth interceptors, and the client attaches the credential to every call.
httpauth command
Example httpauth shows the full tenant-auth wiring for net/http: an operator signs a scoped account token, the server wraps its mux with the auth middleware, and the client signs every request via the transport.
Example httpauth shows the full tenant-auth wiring for net/http: an operator signs a scoped account token, the server wraps its mux with the auth middleware, and the client signs every request via the transport.
internal
manifest
Package manifest reads the valiss.yaml token manifest: the public, non-secret description of the credential tree (operator public keys, accounts with their public keys and scopes, users under each account).
Package manifest reads the valiss.yaml token manifest: the public, non-secret description of the credential tree (operator public keys, accounts with their public keys and scopes, users under each account).
pkg
creds
Package creds implements the client credentials file: the subject's token plus the seed that signs its requests, modeled on the nsc creds format.
Package creds implements the client credentials file: the subject's token plus the seed that signs its requests, modeled on the nsc creds format.
grpcauth
Package grpcauth wires the tenant authentication scheme into gRPC: server interceptors that verify the per-request credential and a client per-RPC credential that attaches it.
Package grpcauth wires the tenant authentication scheme into gRPC: server interceptors that verify the per-request credential and a client per-RPC credential that attaches it.
httpauth
Package httpauth wires the tenant authentication scheme into net/http: a server middleware that verifies the per-request credential and a client http.RoundTripper that attaches it.
Package httpauth wires the tenant authentication scheme into net/http: a server middleware that verifies the per-request credential and a client http.RoundTripper that attaches it.
token
Package token implements the core of the tenant authentication scheme, modeled on NATS operator/account credentials:
Package token implements the core of the tenant authentication scheme, modeled on NATS operator/account credentials:

Jump to

Keyboard shortcuts

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