A policy-driven trust gateway that routes requests using cryptographic visa tokens.
Implementation and demonstration projects:
- VFA-MVP – wallet / merchant reference implementation → https://github.com/Csnyi/VFA-MVP
- VFA-Lab — this repository: architecture sandbox and gateway routing demo
- VFA-cloud-PoC — cloud operation PoC (deployment scenario) → https://github.com/Csnyi/VFA-cloud-PoC
- VFA-Spec - protocol specification → https://github.com/Csnyi/VFA-Spec
⚠ Experimental research prototype
⚠ Not production ready
Minimal, end-to-end demonstration of a policy-driven trust gateway that routes traffic based on a signed visa token.
This lab shows how an application-layer decision engine can dynamically route requests between sandbox and production backends using cryptographic proof.
- Policy-driven routing at the gateway
- Signed visa token verification
- Automatic downgrade to sandbox
- Token revocation
- Zero-trust style access control
- Developer-friendly observability
flowchart TB
subgraph Client
U["User"]
W["Identity Wallet"]
end
subgraph Gateway
G["VFA Gateway"]
P["Policy Engine"]
end
subgraph Services
PROD["Production Service"]
SB["Sandbox Service"]
end
U -->|"intent"| W
W -->|"VFA visa token"| G
U -->|"HTTPS request + token"| G
G --> P
P -->|"valid"| PROD
P -->|"invalid / missing"| SB
PROD -->|"response"| U
SB -->|"limited response"| U
sequenceDiagram
autonumber
participant C as Client
participant W as Identity Wallet
participant G as VFA Gateway
participant S as Backend Service
Note over C,W: Intent preparation
C->>W: Request visa token (scope, ttl)
W-->>C: Signed VFA visa token
Note over C,G: TLS handshake with VFA extension
C->>G: ClientHello + vfa_token
G->>G: Verify token signature & policy
alt Valid token
G-->>C: ServerHello + VFA accepted
Note over C,G: Secure session established
C->>G: HTTPS request
G->>S: Route to production backend
S-->>C: Response
else Invalid or missing token
G-->>C: ServerHello (sandbox policy)
C->>G: HTTPS request
G->>S: Route to sandbox backend
S-->>C: Limited response
end
VFA introduces an optional handshake extension where a cryptographically signed visa token can be presented during connection establishment.
The gateway evaluates the token and decides whether the request should be routed to production services or sandbox environments.
This approach enables policy-driven trust decisions before application logic is executed.
| Service | Role |
|---|---|
| gateway | Decision engine + reverse proxy |
| merchant | Production backend |
| sandbox | Limited backend |
| client | Demo UI |
- Docker Engine 24+
- Docker Compose V2
- curl (optional)
- jq (optional, for pretty output)
docker compose up -d --buildThe demo client is served by the gateway.
Once the gateway is running, open:
http://localhost:8080
The client files are located in:
gateway/client/
The gateway exposes them via:
app.use("/", express.static(path.join(__dirname, "client")));After starting the stack you should be able to:
-
Open the demo UI
http://localhost:8080 -
Issue a visa token
-
Call the API with and without the token and observe routing decisions.
| Service | URL |
|---|---|
| Gateway + Client | http://localhost:8080 |
| Merchant (direct) | http://localhost:5000 |
| Sandbox (direct) | http://localhost:5001 |
GET /api/hello
→ routed to SANDBOX
POST /issue → visaToken
GET /api/hello (Authorization: Bearer …)
→ routed to PROD
curl -X POST http://localhost:8080/issue \
-H "Content-Type: application/json" \
-d '{"ttl_ms":60000}'curl http://localhost:8080/api/hello \
-H "Authorization: Bearer <visaToken>"curl -X POST http://localhost:8080/revoke \
-H "Content-Type: application/json" \
-d '{"visaToken":"<visaToken>"}'Gateway behavior is controlled via environment variables.
Controls behavior when no token is present.
| Value | Behavior |
|---|---|
| sandbox | default → limited access |
| prod | allow full access |
| deny | block request |
Example in docker-compose.yml:
DEFAULT_ROUTE=sandboxThe gateway evaluates:
- Token present?
- Signature valid?
- Token revoked?
- Token expired?
Then routes:
valid token → PROD
invalid/missing → SANDBOX (or DENY by policy)
Gateway logs include:
- timestamp
- path
- decision
- reason
- token id
Example:
{
"decision": "sandbox",
"reason": "missing_token"
}curl http://localhost:8080/api/helloTOKEN=$(curl -s -X POST http://localhost:8080/issue \
-H "Content-Type: application/json" \
-d '{"ttl_ms":60000}' | jq -r .visaToken)
curl http://localhost:8080/api/hello \
-H "Authorization: Bearer $TOKEN"This is an MVP lab environment.
Not included (yet):
- audience binding
- device binding
- nonce / replay protection
- distributed revocation
- key rotation
- scope-based routing
- risk scoring
- ECDSA tokens
- mTLS upstream
- distributed revocation list
- hardware-backed keys
Apache License Version 2.0, January 2004 http://www.apache.org/licenses/
Copyright 2026 Sandor Csicsai
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
VFA (Virtual Flow Agreement) explores a policy-driven trust layer that can operate between clients and services without modifying the application protocol itself.
This repository is a demonstration environment.
The implementation included here is intended only to illustrate the VFA policy decision concept and must not be used in production systems.
The project uses a shared HMAC secret for demonstration purposes.
This mechanism is intentionally simplified and does not represent a production-grade security design.
Any real deployment MUST implement:
- secure key storage
- key rotation
- audience binding
- replay protection
- proper authentication hardening
For further details and recommended production practices see SECURITY.md
FlowAccord concept demo
request token
api/hello
api/data
revocation

flowchart LR
%% Actors
U["User / Client<br/>Browser / App / CLI"] -->|HTTPS request| G
%% Policy plane
subgraph P["VFA Policy Plane (L3.5 overlay)"]
G["Policy Gateway<br/>Decision: prod / sandbox / deny"]
R[("Revocation Store<br/>JSON/SQLite")]
G <-->|read/update| R
end
%% Upstreams
G -->|prod route| M["Merchant API (PROD)<br/>Business endpoints"]
G -->|sandbox route| S["Sandbox API<br/>Limited / safe responses"]
%% Identity plane
subgraph I["VFA Identity Plane (MVP)"]
W["Wallet UI / Wallet Service<br/>issue & manage visaToken"]
V["Token Verify Logic<br/>(HMAC now, ECDSA later)"]
W -->|issue token| U
G -->|verify token| V
end
%% Notes
U -. optional token .-> G
sequenceDiagram
autonumber
participant C as Client
participant G as Policy Gateway (L3.5)
participant R as Revocation Store
participant M as Merchant (PROD)
participant S as Sandbox
C->>G: HTTP(S) request /api/* (optional: Bearer visaToken)
G->>R: Load revocations (tokenId)
alt Token valid & not revoked
G->>M: Proxy request (decision=prod)
M-->>G: Response (full)
G-->>C: Response
else Missing/invalid/revoked token
alt DEFAULT_ROUTE = deny
G-->>C: 403 Denied by policy
else DEFAULT_ROUTE = sandbox
G->>S: Proxy request (decision=sandbox)
S-->>G: Response (limited)
G-->>C: Response
end
end
flowchart TB
Title["Communication stack (mental model)"]
subgraph Stack
direction TB
L2["L2: Link (Ethernet/Wi-Fi)"]
L3["L3: IP routing"]
L35["L3.5: VFA Policy Overlay<br/>(decision plane, optional)"]
L4["L4: TCP"]
L5["L5: TLS"]
L7["L7: Application (HTTP/API)"]
end
Title --> L2
L2 --> L3 --> L4 --> L5 --> L7
L35 -. influences .-> L5
L35 -. routes .-> L7