This guide shows you how to let programs call an HTTP endpoint that has no Package, and how to give that endpoint a credential the program never sees. The example is GitHub’s REST API with a personal access token. Substitute your host and its authentication.
Authorization proxy
Section titled “Authorization proxy”The program can’t hold the credential itself. The model can be talked into
repeating anything generated code can read, so secrets.get is refused
from main whatever the Blueprint says. The Blueprint names the secret and
the host, and the authorization proxy adds the credential to each
matching request outside the program. The program sends a plain request and
sees the response, but never the header.
Allow the request
Section titled “Allow the request”submilli:http has one capability per verb, plus one for downloads, and
http.<method> for any other method http.request sends. List
them, with the fields a filter can test and the rules the Blueprint already
has for each:
submilli blueprint capability list submilli:httpsubmilli:http http.get — HTTP GET fields: host: string, path: string, body_size: number, timeout_ms: number example filter: host == "api.example.com" http.post — HTTP POST fields: host: string, path: string, body_size: number, timeout_ms: number example filter: host == "api.example.com" http.put — HTTP PUT fields: host: string, path: string, body_size: number, timeout_ms: number example filter: host == "api.example.com" http.patch — HTTP PATCH fields: host: string, path: string, body_size: number, timeout_ms: number example filter: host == "api.example.com" http.delete — HTTP DELETE fields: host: string, path: string, body_size: number, timeout_ms: number example filter: host == "api.example.com" http.head — HTTP HEAD fields: host: string, path: string, body_size: number, timeout_ms: number example filter: host == "api.example.com" http.options — HTTP OPTIONS fields: host: string, path: string, body_size: number, timeout_ms: number example filter: host == "api.example.com" http.download — Download a URL straight to the VFS fields: host: string, url_path: string, vfs_path: string, max_bytes: number, overwrite: boolean, decompress: boolean example filter: host == "cdn.example.com" and overwrite == false http.<method> — Any other HTTP method, through `http.request`: `http.trace` gates TRACE fields: host: string, path: string, body_size: number, timeout_ms: number example filter: host == "api.example.com"Grant the verb the program needs, narrowed to the host:
submilli blueprint capability add http.get --filter 'host == "api.github.com"'✓ added allow http.get (filter: host == "api.github.com") to caller 'main' in blueprint.yaml HTTP GET filter fields: host: string, path: string, body_size: number, timeout_ms: numberThis allows GET alone. If the program also posts, add an http.post rule.
Each redirect is checked before it is sent, under the same rules.
Run a program
Section titled “Run a program”Create rate.ts. It asks GitHub how many requests the caller has left
this hour, which GitHub answers differently for an anonymous caller and
for one with a token:
import * as http from "submilli:http";
interface RateLimit { resources: { core: { limit: number; remaining: number } };}
function main(): string { const headers = new Map<string, string>(); headers.set("User-Agent", "submilli"); const response = http.get("https://api.github.com/rate_limit", headers); response.throwForStatus(); const core = (response.json() as RateLimit).resources.core; return `${core.remaining} of ${core.limit} requests left this hour`;}submilli run --blueprint blueprint.yaml rate.ts51 of 60 requests left this hourThe request went through as an anonymous caller, and GitHub allows sixty
of those an hour. GitHub requires the User-Agent header and answers 403
without one.
Add the credential
Section titled “Add the credential”Declare the token, store its value, and tell the proxy to add it to requests for that host:
submilli blueprint secret add GITHUB_TOKEN --store github_tokensubmilli secret put github_tokensubmilli blueprint auth-proxy add --host api.github.com --bearer GITHUB_TOKEN✓ declared secret 'GITHUB_TOKEN' (store: github_token) in blueprint.yamlValue for 'github_token': [hidden]Stored secret 'github_token'✓ added auth_proxy rule for host 'api.github.com' (bearer auth) in blueprint.yamlsecrets: GITHUB_TOKEN: store: github_tokenauth_proxy:- host: api.github.com auth: bearer: GITHUB_TOKENpermissions: main: - capability: http.get filter: host == "api.github.com" action: allowRun the same program again, unchanged:
submilli run --blueprint blueprint.yaml rate.ts5000 of 5000 requests left this hourGitHub now sees the token, and the program never did.
An entry applies to requests whose host matches it exactly. If the
endpoint wants basic auth, use --basic-username and --basic-password.
For anything else, use --header 'Name: value' or --query, with
${secrets.NAME} in the value. With a credential injected, a redirect may
go only to the same scheme, host, and port.
Register it on a server
Section titled “Register it on a server”The server has its own secret store. Put the token there before you
register the Blueprint, because registration checks that every store:
secret exists:
submilli server secret put github_tokensubmilli server blueprint apply blueprint.yamlValue for 'github_token': [hidden]Stored secret 'github_token'Added blueprint 'support'Then run the program there, the way an application would:
submilli server run-code rate.ts --blueprint support5000 of 5000 requests left this hourIf the endpoint is plain HTTP
Section titled “If the endpoint is plain HTTP”Blueprints require HTTPS. For a host that speaks only HTTP, such as an internal service, opt in twice. Set the flag on the proxy entry for that host and at the top level of the file. The top-level flag has no command:
submilli blueprint secret add LEGACY_TOKEN --store legacy_tokensubmilli blueprint capability add http.get --filter 'host == "legacy.internal.acme.com"'submilli blueprint auth-proxy add --host legacy.internal.acme.com --bearer LEGACY_TOKEN --allow-insecure-http✓ declared secret 'LEGACY_TOKEN' (store: legacy_token) in blueprint.yaml✓ added allow http.get (filter: host == "legacy.internal.acme.com") to caller 'main' in blueprint.yaml HTTP GET filter fields: host: string, path: string, body_size: number, timeout_ms: number✓ added auth_proxy rule for host 'legacy.internal.acme.com' (bearer auth) in blueprint.yamlallow_insecure_http: trueauth_proxy:- host: legacy.internal.acme.com allow_insecure_http: true auth: bearer: LEGACY_TOKENBoth flags default to false. The top-level one covers all HTTP calls
the Blueprint allows, including a Package’s. The entry’s one covers the
credential.