Expose exactly the faces of a running Spring service you choose — securely, over gRPC.
A facet is a deliberately cut, polished face of a prism. Facet by Wolfigs brings that idea to the JVM: annotate any Spring bean method or field, and it becomes a secured, self-documenting gRPC endpoint — callable from any language, introspectable at runtime, with OAuth 2.0 and API-key authentication built in.
@Service
public class AppService {
@Facet(description = "Current version")
public String getVersion() { return "1.4.2"; }
@SecuredFacet(auth = AuthType.API_KEY, description = "Toggle maintenance mode")
private boolean maintenanceMode = false;
}grpcurl -plaintext \
-d '{"bean_name": "AppService", "member_name": "getVersion"}' \
localhost:9090 facet.FacetService/InvokeMethod
# → { "result": "1.4.2" }No proto files to write. No controllers. No redeploy to see what a service exposes — GetDocs returns the live, typed surface of every running instance.
Modern JVM services have no good answer for secure runtime access:
- JMX is effectively legacy — RMI-based, firewall-hostile, JVM-only.
- Spring Actuator is HTTP-only, coarse-grained, and awkward to secure per-operation.
- Hand-rolled admin endpoints multiply across every service, each with its own auth bugs.
Facet replaces all three with one annotation model: per-member authentication (OAuth 2.0 JWT and/or API keys), live introspection, typed invocation from Java, Python, or any gRPC-speaking language, and streaming when you need it.
Typical uses:
- Operations — feature flags, maintenance toggles, cache flushes, reindex triggers, guarded by per-operation credentials
- Runtime introspection — read live state of any bean without a debugger or redeploy
- Internal tooling & backoffice — call service internals from scripts and admin UIs without building REST layers
- Zero-ceremony internal RPC — typed Java-to-Java calls before you've formalized a contract
| Module | Artifact | What it does |
|---|---|---|
| facet-server | com.wolfigs:facet-server |
Exposes annotated bean methods/fields as gRPC endpoints with built-in auth |
| facet-client | com.wolfigs:facet-client |
Turns remote facets into plain Java calls via declarative interfaces or a fluent template |
Server — expose a bean:
<dependency>
<groupId>com.wolfigs</groupId>
<artifactId>facet-server</artifactId>
<version>0.2.0</version>
</dependency>facet:
server:
grpc:
port: 9090Annotate with @Facet (public) or @SecuredFacet(auth = ...) (OAuth 2.0 / API key / both). Auto-configuration does the rest. Full guide: facet-server/README.md
Client — call it like a local service:
<dependency>
<groupId>com.wolfigs</groupId>
<artifactId>facet-client</artifactId>
<version>0.2.0</version>
</dependency>@FacetClient(server = "inventory-service", beanName = "InventoryService")
public interface InventoryClient {
int getStock(String productId);
Mono<Product> getProduct(String productId); // reactive supported
}Full guide: facet-client/README.md
Exposure & invocation
- Per-member auth: OAuth 2.0 JWT (RS256/384/512, ES256/384/512) and/or API keys, key rotation without downtime
- Scope-based authorization —
@SecuredFacet(scopes = {"ops:write"})enforced against JWTscope/scpclaims - Live introspection via
GetDocs— every exposed member with full signatures, plus a surface fingerprint for drift detection - Field read and write (
SetAttribute, returns previous value; writes are denied by default and require explicit server opt-in) - Overload resolution from argument types, with explicit
parameter_typesdisambiguation - Reactive returns (
Mono,Flux,CompletableFuture) unwrapped automatically - Bidirectional streaming, batch streaming, stateful stream sessions
- Interface scanning with automatic name aliases; inherited and static members supported
Production hardening
- TLS and mutual TLS on both server and client (PEM cert/key, client-auth
NONE/OPTIONAL/REQUIRE) - Deadlines per server (
deadline-ms) with per-call fluent override - Retries with exponential backoff —
UNAVAILABLEalways;DEADLINE_EXCEEDEDonly on@Idempotentmethods - Contract verification at startup —
verify-contract: truechecks every@FacetClientinterface against the server's live surface and fails fast on drift - Structured audit log — every invocation on the dedicated
com.wolfigs.facet.AUDITlogger with principal, outcome, and latency - Micrometer metrics — per-member counters and timers, auto-activated when a
MeterRegistryis present - W3C trace-context propagation —
traceparent/tracestate/baggage/x-correlation-idflow across hops via the MDC - Standard gRPC health service (
grpc.health.v1.Health) for load balancers and Kubernetes probes - Graceful shutdown — drains in-flight calls within a configurable grace period
- Spring Boot auto-configuration — one annotation and a port number
Deny by default, opt in deliberately:
- Nothing is exposed unless explicitly annotated
- Remote field writes are disabled unless the server sets
facet.server.security.allow-field-writes: true - Secured members require the credential type you declare — wrong credential type is
PERMISSION_DENIED, missing isUNAUTHENTICATED - Declared scopes must all be present in the validated token; an API key can never satisfy a scoped member
- Declaring scopes on an API-key-only member is rejected at startup (it would silently enforce nothing)
- Every invocation — allowed or denied — lands in the audit log
See each module's SECURITY.md and the best practices.
- Prism — build like a monolith, deploy like microservices: a build-time topology compiler for Java/Spring
- Facet — expose exactly the runtime faces of your services you choose
Facet is the successor of FractalX NetScope, rebuilt under com.wolfigs.facet.
Apache License 2.0 — see LICENSE.
- Sathnindu Kottage — @sathninduk
- Wolfigs — github.com/wolfigs