Sitelet https://github.com/jedarden/SEAM
Skip to content

Latest commit

 

History

691 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SEAM

Self-documenting Endpoint Access Mediator — a single unified HTTP endpoint that proxies to multiple authenticated backend services, injecting the required secret into each request server-side (from OpenBao) before forwarding it on, then passing the response back. Calling agents never see the secret; malformed requests are guided toward the correct shape via a self-describing OpenAPI spec instead of a bare error.

Structure

  • docs/notes/ — features, constraints, design decisions
  • docs/research/ — external reference material and prior art
  • docs/plan/plan.md — complete application plan

Running SEAM

Starting the Server

seam serve [flags]

Configuration Flags

seam serve takes fifteen configuration flags. All fifteen are listed here and all fifteen have a SEAM_* environment counterpart (see Environment Variables); the two lists describe the same set.

Server Ports

  • --caller-port (default: 8080) - Port for the caller-facing listener
  • --operator-port (default: 8081) - Port for the operator-only listener

Server Configuration

  • --base-url (default: http://localhost:8080) - Base URL for the caller-facing interface
  • --spec-dir (default: ./spec) - Directory containing local OpenAPI spec files

Fragments & Schema

  • --fragment-mode (default: false) - Enable fragment merge mode (routes are read from --fragments-dir; see Fragment directory and hot-reload scope)
  • --schema-path (default: ./spec/route-fragment-schema.json) - Path to the route-fragment JSON schema for validation
  • --fragments-dir (default: ./fragments) - Directory containing OpenAPI fragment files
  • --enable-hot-reload (default: false) - Enable file-watch hot reload of route fragments

Corpus Capture

  • --capture-enabled (default: false) - Enable HTTP request/response capture for corpus collection
  • --corpus-dir (default: corpus) - Directory to store captured corpus files

Upstream Trust

  • --upstream-ca-dir (default: /etc/gateway/upstream-ca) - Directory for upstream CA bundles
  • --allowlist-file (default: none) - Path to the upstream host allowlist

Vault Path & Body Limits

  • --vault-base-dir (default: rs-manager/rs-manager/seam/routes) - Base directory that x-vault-path must nest x-seam-owner under
  • --max-replayable-request-bytes (default: 1048576) - Maximum request body size buffered for replay, in bytes
  • --max-buffered-response-bytes (default: 1048576) - Maximum decoded response body size held for whole-response scrubbing, in bytes (see the note below)

In-cluster refusal (upstream trust). When both KUBERNETES_SERVICE_HOST and KUBERNETES_PORT are set, SEAM treats the process as running in-cluster and strips operator-supplied overrides of the two upstream-trust paths: a custom --upstream-ca-dir / SEAM_UPSTREAM_CA_DIR is refused with a warning and /etc/gateway/upstream-ca is used instead, and the allowlist is always the operator-mounted /etc/gateway/allowlist.yaml — a supplied --allowlist-file / SEAM_UPSTREAM_ALLOWLIST can never replace that mounted control inside a pod. The refusal applies to supplied values only; the defaults are exactly those in-cluster paths. Outside a cluster both flags accept any path. The precedence is mounted control > flag > environment variable > default: an explicit flag already beats SEAM_UPSTREAM_CA_DIR / SEAM_UPSTREAM_ALLOWLIST before the boundary runs, so what gets refused in-cluster is the flag's value.

Operator-visible failure modes (in-cluster upstream trust).

  • Refused override. Each refused value is announced at startup as [config] WARNING: --upstream-ca-dir is refused in-cluster; using /etc/gateway/upstream-ca (likewise for --allowlist-file). The warning names the flag and the mounted path that replaces it and never echoes the refused value; startup continues on the mounted controls. This is expected in a Deployment that still carries a developer-era SEAM_UPSTREAM_* variable — remove the variable to silence it.
  • Mounted allowlist unavailable. If /etc/gateway/allowlist.yaml is missing or unreadable at startup, SEAM logs [Allowlist] Failed to load allowlist from /etc/gateway/allowlist.yaml: … and continues fail-closed: /readyz withholds readiness (allowlist dependency down, 503), and every upstream dispatch is refused with allowlist_parse_failed. The enforcer is built once at startup — hot-reload watch events on the allowlist mount drive the route-reload pipeline only — so restoring the mount takes effect after a pod restart.
  • Mounted CA bundle unavailable. If a route's x-upstream-tls.caBundle names a file absent from /etc/gateway/upstream-ca, dispatches for that route fail with 503 proxy_creation_failed plus a request ID; the pod log carries the cause naming the exact missing path under the mount (failed to read CA bundle /etc/gateway/upstream-ca/<name>: …), which the response body deliberately omits. CA clients are built lazily on a route's first dispatch and failed builds are not cached, so restoring the bundle recovers on the next dispatch without a restart.

Fragment directory and hot-reload scope

In fragment mode (--fragment-mode / SEAM_FRAGMENT_MODE) every route SEAM serves is merged from one directory, resolved once at startup:

  1. an explicitly passed --fragments-dir,
  2. otherwise a non-empty SEAM_FRAGMENTS_DIR (an empty value counts as unset),
  3. otherwise the built-in default ./fragments.

That resolved directory is authoritative. --spec-dir never influences which fragments are loaded — it only selects the static-spec location in non-fragment mode. <spec-dir>/fragments.d is not the fragment location by default: it survives only as a legacy fallback applied when the resolved directory is empty, which the serve path cannot reach (the flag default is non-empty) — it exists for an explicitly passed --fragments-dir= and for direct loader-API callers. seam lint and seam diff resolve --fragments-dir with their own flag-over-environment rule (see lint / diff explicit-flag tracking) and never consult --spec-dir.

When --enable-hot-reload / SEAM_HOT_RELOAD_ENABLED is on, the watcher roots at that same resolved directory: it watches the directory itself plus each immediate subdirectory (the per-service mounts a ConfigMap projection creates), together with the allowlist file's directory and the upstream CA directory when configured. A missing fragments directory is logged and simply means nothing is watched — not a startup error — and a directory created after startup is picked up only on restart. A watch event re-reads the directory captured at startup: changing SEAM_FRAGMENTS_DIR in a running process's environment never redirects a reload. Each reload re-walks the tree, re-merges, and atomically swaps the route table (in-flight requests finish on the old table); a re-merge whose hash matches the current spec is a no-op. Schema validation runs at startup only — a reload does not re-validate fragments against --schema-path, so seam lint remains the gate for fragment structure.

Environment Variables

Every serve configuration flag can also be set via an environment variable with the SEAM_ prefix — the table covers all fifteen flags above:

Variable Flag Default
SEAM_CALLER_PORT --caller-port 8080
SEAM_OPERATOR_PORT --operator-port 8081
SEAM_BASE_URL --base-url http://localhost:8080
SEAM_SPEC_DIR --spec-dir ./spec
SEAM_FRAGMENT_MODE --fragment-mode false
SEAM_SCHEMA_PATH --schema-path ./spec/route-fragment-schema.json
SEAM_CAPTURE_ENABLED --capture-enabled false
SEAM_CORPUS_DIR --corpus-dir corpus
SEAM_FRAGMENTS_DIR --fragments-dir ./fragments
SEAM_UPSTREAM_CA_DIR --upstream-ca-dir /etc/gateway/upstream-ca (override refused in-cluster)
SEAM_UPSTREAM_ALLOWLIST --allowlist-file none (override refused in-cluster)
SEAM_VAULT_BASE_DIR --vault-base-dir rs-manager/rs-manager/seam/routes
SEAM_MAX_REPLAYABLE_REQUEST_BYTES --max-replayable-request-bytes 1048576
SEAM_MAX_BUFFERED_RESPONSE_BYTES --max-buffered-response-bytes 1048576
SEAM_HOT_RELOAD_ENABLED --enable-hot-reload false

The pairing rule is mechanical — SEAM_ plus the flag name upper-cased with dashes as underscores — and thirteen of the fifteen variables follow it. Two are paired by meaning instead and break the derivation: SEAM_UPSTREAM_ALLOWLIST maps to --allowlist-file (not SEAM_ALLOWLIST_FILE, keeping the allowlist's upstream identity in the variable name), and SEAM_HOT_RELOAD_ENABLED maps to --enable-hot-reload (not SEAM_ENABLE_HOT_RELOAD). Where the rule and the table disagree, the table is authoritative.

--max-buffered-response-bytes bounds only how much of a response SEAM may hold in memory for whole-body secret scrubbing; it is never a scrubbability limit and never rejects a response. A response whose declared Content-Length is at or under the cap is scrubbed whole and returned with its (recomputed) Content-Length. A response over the cap — or with no declared length, or whose decoded size exceeds the cap even when its encoded size does not — is scrubbed incrementally with bounded memory and streamed to the caller chunked (Content-Length removed) with the same status, headers, trailers, and content-encoding; nothing is truncated. A non-positive value falls back to the default. The two size caps are independent knobs: this one governs responses only, --max-replayable-request-bytes governs request replay only, and tuning one never moves the other.

Precedence (serve)

Flags win over the environment. An explicitly passed command-line flag takes precedence over a non-empty corresponding SEAM_* variable. Environment values fill flags that were not passed, and built-in defaults fill everything left unset. This gives operators an environment-based deployment default while preserving an explicit CLI override. seam healthcheck follows the same rule for SEAM_CALLER_PORT, so an explicit probe flag wins while an environment-only port override is still honoured.

A variable set to the empty string counts as unset: the flag value survives.

Invalid values

  • Integer variables (*_PORT, *_BYTES) parse as an optional sign followed by digits. Leading whitespace is skipped and anything after the integer prefix is ignored: SEAM_CALLER_PORT=8080abc configures 8080, and 0x10 configures 0. A value with no leading integer is rejected — the previous environment/default value is kept and a warning is logged. An explicit flag still wins without parsing the environment value. There is no range validation at configuration time: an out-of-range port such as -5 or 99999 is applied and fails later, when the listener binds.
  • Boolean variables (SEAM_FRAGMENT_MODE, SEAM_CAPTURE_ENABLED, SEAM_HOT_RELOAD_ENABLED) recognize exactly true and 1, lowercase. Any other non-empty value — including TRUE, yes, 0 and false — means false when the environment supplies the setting; an explicit flag still wins. SEAM_HOT_RELOAD_ENABLED is deliberately asymmetric: only true/1 changes an unset flag, so an environment value can enable hot reload but cannot override an explicit flag.
  • SEAM_VAULT_BASE_DIR is whitespace-trimmed, fills an omitted flag, and falls back to the shared default when neither names a prefix.

lint / diff explicit-flag tracking

seam lint and seam diff follow the same flag-over-environment rule for SEAM_FRAGMENTS_DIR, SEAM_SCHEMA_PATH and SEAM_UPSTREAM_ALLOWLIST: the environment fills the corresponding flag only while it is still at its default. (Corollary: passing the default value explicitly, e.g. --fragments-dir ./fragments, is indistinguishable from omitting the flag.) seam import reads no SEAM_* configuration.

Other SEAM_* variables (SEAM_OPENBAO_ADDR, SEAM_OPENBAO_SA_TOKEN_PATH, SEAM_TEST_IDENTITY_MODE, …) are server-runtime knobs, not CLI configuration.

seam healthcheck

Probe the caller-facing liveness endpoint. This is what the container image's HEALTHCHECK invokes — the runtime image is FROM scratch and has no shell, so the probe must be a real subcommand.

seam healthcheck [--caller-port <port>] [--timeout <duration>]
  • --caller-port (default: 8080) - Port of the caller-facing listener to probe
  • --timeout (default: 2s) - Probe timeout

Issues GET http://127.0.0.1:<port>/_seam/healthz and requires HTTP 200. SEAM_CALLER_PORT fills the port only when --caller-port was not passed explicitly — the same flag-over-environment rule as serve (see Precedence (serve)) — so a port override configured on the Deployment is honoured without beating an explicit flag.

Exit codes: 0 the gateway answered 200; 1 the probe failed or timed out; 2 invalid usage.

seam lint

Validate route fragments against route-fragment-schema.json plus SEAM's structural checks: the x-seam-owner chain (owner must match the fragment's parent directory and be nested by x-vault-path), authored x-api-version shape and placement, reserved control-plane paths, upstream URLs (well-formed absolute http(s), no IP-literal hosts, membership of the operator allowlist when one is supplied), transport acknowledgements (plaintext upstreams, insecureSkipVerify, unscrubbable responses), and route guards (quota unit mismatches, breaker disagreements across same-origin routes). Spec and structural violations are errors; acknowledgement items that need human review are warnings.

seam lint [flags] [path ...]
  • --fragments-dir, --fragments (default: ./fragments) - Directory containing route fragments
  • --schema-path, --schema (default: ./spec/route-fragment-schema.json) - Path to route-fragment-schema.json
  • --upstream-allowlist, --upstream-allowlist-path, --allowlist-path, --allowlist (default: none) - Operator-owned upstream-host allowlist; absent is inert
  • --json - Emit a machine-readable JSON report (LintReport) instead of text

Positional arguments select what is linted: none means the --fragments-dir directory, a single directory means that directory, and one or more file paths means exactly those files. Flags may also appear after positional paths, so shell-expanded globs behave as expected:

seam lint                                    # lint ./fragments
seam lint fragments/github-api               # lint one fragment directory
seam lint fragments/*/fragment.yaml --json   # lint explicit files, JSON report

SEAM_FRAGMENTS_DIR, SEAM_SCHEMA_PATH and SEAM_UPSTREAM_ALLOWLIST fill the corresponding flag only while it is still at its default (see lint / diff explicit-flag tracking).

Exit codes: 0 lint passed (warnings allowed); 1 at least one error finding; 2 usage or setup failure — unknown flag, unreadable path, a schema that does not compile, or an unwritable report.

seam diff

Merge the current route fragments into a spec and compare it against a base version — by default the fragments at git HEAD, extracted with git archive. Use it to see which routes a fragment change adds, removes, or modifies before pushing.

seam diff [flags]
  • --fragments-dir, -f (default: ./fragments) - Directory containing route fragments
  • --base, -b (default: git HEAD's fragments/) - Base directory to compare against
  • --json, -j - Emit the machine-readable DiffResult (paths_added, paths_removed, paths_modified, summary.has_changes) instead of the text report
  • --output, -o - Additionally write the merged current spec to this file
  • --unified (default: true), --side-by-side - Presentation switches; the current writer emits the same structured change report for either

Behaviour worth knowing:

  • Positional path arguments are rejected — pass --fragments-dir instead.
  • Outside a git repository (or if git archive fails) there is no implicit base: pass --base explicitly or the command exits 2.
  • A --fragments-dir that is mistyped or holds no fragments is refused (2) — "no fragments loaded" is not the same as "no changes". An empty base is allowed, with a warning that every current path will be reported as added.
  • SEAM_FRAGMENTS_DIR fills --fragments-dir only while it is still at its default.

Exit codes: 0 the merged specs are identical; 1 changes detected; 2 setup failure — usage error, no resolvable base, no current fragments, or a load/merge/compare/write failure.

seam import

Fetch an OpenAPI 3.x (or Swagger 2.0) spec over HTTP(S) and generate a curatable bootstrap fragment. The generated file carries x-seam-schema: v1, x-seam-owner, x-upstream (derived from the spec URL as scheme://host[:port] — the path that located the spec document is not part of the upstream) and the imported paths. The credential and access decisions are deliberately left to the curator: the output reminds you to add x-vault-path, x-inject-as, x-required-scope and any TLS/cache knobs by hand. An http:// upstream emits x-upstream-plaintext: acknowledged with a loud note. Swagger 2.0 sources are accepted; their top-level definitions/parameters are carried across as components.schemas/components.parameters so $refs inside imported operations keep resolving. The command reads no SEAM_* configuration.

seam import --from-url <url> [flags]
  • --from-url, -u (required) - URL of the OpenAPI spec to import; must be http or https
  • --owner, -o (default: derived from the URL host) - Owner/service name for the fragment
  • --output, -f (default: <owner>/fragment.yaml) - Output fragment file path
  • --paths, -p - Comma-separated list of paths to import (default: all)
  • --methods, -m - Comma-separated list of HTTP methods to import, case-insensitive (default: all)
  • --filter-prefix - Only import paths with this prefix
  • --strip-prefix - Strip this prefix from imported paths
  • --add-prefix - Add this prefix to all imported paths
  • --timeout (default: 30s) - HTTP timeout for fetching the spec
# Whole spec: owner and output path are derived from the URL
seam import --from-url https://api.example.com/openapi.json
#   -> owner api-example-com, written to api-example-com/fragment.yaml

# Curated import into the fragments tree
seam import -u https://internal.example.com/swagger.json \
  --owner github-api --output fragments/github-api/fragment.yaml \
  --filter-prefix /api/v1/ --strip-prefix /api/v1 --methods get,post

# Then validate — seam lint checks x-seam-owner against the parent directory name,
# so keep the fragment in a directory named after the owner
seam lint fragments/github-api/fragment.yaml

Exit codes: 0 fragment written; 1 no paths matched the filter criteria; 2 failure — missing or invalid --from-url, a non-http(s) scheme, a fetch/HTTP error, a spec that parses as neither JSON nor YAML, or an unwritable output path.

seam retirement-handoff

Turn one detection-only evaluator finding into a reviewed, linted fragment change. The command defaults to a no-write plan. --apply writes only the explicit --target after the real SEAM lint gate passes; it never runs Git, kubectl, or a git-host API. A ConfigMap target requires --data-key, because the evaluator's fragment_path is a locator rather than a writable path.

seam retirement-handoff \
  --finding evaluator-finding.jsonl \
  --target /path/to/declarative-config/k8s/rs-manager/seam/configmap-routes-legacy.yaml \
  --data-key legacy.yaml \
  --schema /path/to/SEAM/spec/route-fragment-schema.json

# Review the plan, then apply before committing the declarative-config change.
seam retirement-handoff \
  --finding evaluator-finding.jsonl \
  --target /path/to/declarative-config/k8s/rs-manager/seam/configmap-routes-legacy.yaml \
  --data-key legacy.yaml \
  --schema /path/to/SEAM/spec/route-fragment-schema.json \
  --apply

# After commit/push and ArgoCD reconciliation, wait for the reload counter.
seam retirement-handoff --observe-only \
  --observe-url http://seam-operator.example/health/upstreams

The command inserts x-seam-deprecated at the fragment root, refuses to overwrite an existing marker, and leaves the target unchanged when lint fails. After the operator commits and pushes the manifest, ArgoCD reconciles it; the observed reload is the proof that SEAM picked up the change. If a caller returns, revert that declarative-config landing commit with ordinary git revert; the same hot-reload observation confirms the marker disappeared.

Differential replay (tools/diffharness)

lint, diff and import manage fragments; the differential replay harness in tools/diffharness is the conformance gate that decides whether a fragment may ship at all. It replays a captured corpus of real request/response pairs against both the incumbent proxy and SEAM, then compares the responses for equivalence — a service's fragment does not ship, and its migration prose is not deleted, until its corpus passes the replay. The tools live in their own Go module and have their own README; this section covers the workflow, the harness README covers the full corpus format and comparison rules.

Build both tools from the harness module:

cd tools/diffharness
go build -o seam-capture ./cmd/seam-capture
go build -o seam-replay ./cmd/seam-replay

seam-capture — recording proxy

seam-capture \
  --incumbent https://service.example.com \
  --service service \
  --corpus service-corpus.json \
  --listen :8080

A transparent proxy that forwards every request to --incumbent and records the exchange (status, headers, body) into the --corpus JSON file. Credential-bearing headers are redacted before the corpus is written; the matching secret references are added to the corpus by hand afterwards (see Secret-reference rules). Capture can be turned off with --capture-enabled=false (or SEAM_CAPTURE_ENABLED=false) while keeping transparent forwarding, and bypassed per request with an X-Seam-Capture-Skip header — use it for health checks. Exit status: 0 on a clean shutdown (corpus saved); 2 missing required flags or an invalid --incumbent; a listen/save failure aborts with a logged fatal error.

seam-replay — conformance tester

seam-replay \
  --incumbent https://service.example.com \
  --seam http://localhost:9000 \
  --corpus service-corpus.json \
  --secrets service-secrets.local.json \
  --report service-report.json

Inputs

  • --corpus (required) - Captured corpus JSON (schema: seam-diff-corpus/v1)
  • --incumbent, --seam (required) - Base URLs; every corpus entry is replayed against both
  • --secrets (optional) - JSON file mapping each corpus secret ref to its literal value; falls back to the environment per the rules below
  • --ignore-header (repeatable) - Additional headers excluded from comparison, on top of the volatile defaults (Date, Server, X-Request-Id, Set-Cookie, ETag)
  • --verbose - Log each replay as it runs

Outputs - a human-readable summary on stdout, and with --report a JSON report (passCount/failCount/skipCount plus per-entry verdicts and diffs). The JSON report is the conformance evidence attached to a cutover review.

Exit status: 0 every replayable entry passed; 1 at least one entry FAILED or a fatal setup error (unreadable or invalid corpus, no replayable entries, unwritable report); 2 missing required flags.

Secret-reference rules

  • A corpus carries references, never values. Each entry's secrets array names a vault:<path> ref, and the ref must nest under the enforced vault base (rs-manager/rs-manager/seam/routes by default; set SEAM_VAULT_BASE_DIR to match a capture taken under a different base). A ref outside the base is rejected when the corpus loads, not at replay.
  • Values are resolved in memory at replay time, file over environment: the --secrets file is consulted first, then SEAM_DIFF_SECRET_<REF>, where <REF> is the ref upper-cased with every run of non-[A-Z0-9_] characters collapsed to one _ — so vault:rs-manager/rs-manager/seam/routes/argocd-ro/ro-token resolves from SEAM_DIFF_SECRET_VAULT_RS_MANAGER_RS_MANAGER_SEAM_ROUTES_ARGOCD_RO_RO_TOKEN.
  • Keep the --secrets file out of git — the harness convention names it *.local.json. Populate it from your secret store for the duration of a run.
  • An unresolved ref skips its entry (SKIP: unresolved secret ref) rather than failing it: a configuration gap must not turn a corpus red.
  • A secret that appears byte-identically in a SEAM response is a hard FAIL (secretLeaked in the report). The leak check runs before the comparison and cannot be masked by expected-diff allowances.
  • Neither tool ever writes a secret value to disk — the corpus holds refs, the report holds verdicts.

Examples

Basic Usage (capture disabled)

seam serve

Enable Corpus Capture

# Via command-line flag
seam serve --capture-enabled --corpus-dir ./my-corpus

# Via environment variable
SEAM_CAPTURE_ENABLED=true SEAM_CORPUS_DIR=./my-corpus seam serve

Custom Ports

seam serve --caller-port 9000 --operator-port 9001

Capture Status Endpoint

When capture is enabled, you can check the status via the operator endpoint:

curl http://localhost:8081/_seam/capture/status

Response:

{
  "enabled": true,
  "entry_count": 42,
  "corpus_dir": "corpus"
}

Manual Corpus Save

Trigger an immediate save of the corpus:

curl -X POST http://localhost:8081/_seam/capture/save

Response:

{
  "status": "saved",
  "entry_count": 42
}

Running Benchmarks

SEAM includes a benchmark suite to measure performance characteristics.

Quick Start

Run all benchmarks with default settings:

make benchmark

Benchmark Modes

Standard run (10s per benchmark, memory stats):

go test -bench=. -benchmem ./benches/...

CPU profiling:

make benchmark-cpu
# Analyze with: go tool pprof benchmark-results/cpu.prof

Memory profiling:

make benchmark-mem
# Analyze with: go tool pprof benchmark-results/mem.prof

CI mode (JSON output for automated collection):

make benchmark-ci
# Results: benchmark-results/latest.json

Interpreting Results

Benchmark output shows:

BenchmarkProxyForwarding/GET-8    1000000    1234 ns/op    512 B/op    8 allocs/op
  • 1000000: Iterations executed
  • 1234 ns/op: Nanoseconds per operation
  • 512 B/op: Bytes allocated per operation
  • 8 allocs/op: Number of memory allocations per operation

For detailed benchmark documentation, see benches/README.md.

Baselines and Regression Checks

Capture a baseline for a benchmark category, then compare later runs against it. The check exits non-zero when a metric regresses by more than 10% (or the THRESHOLD supplied by the caller).

make benchmark-save-baseline TYPE=throughput
make benchmark-check-regression TYPE=throughput
make benchmark-check-regression TYPE=memory THRESHOLD=15

Development

Building

make build

Testing

make test

Dependencies

make deps

Part of jedarden.com

This GitHub repo is a read-only mirror of git.ardenone.com/jedarden/SEAM — issues and PRs are welcome here either way.

About

Self-documenting Endpoint Access Mediator - unified secret-injecting proxy gateway

Topics

Resources

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages