docs: access model, geodata lifecycle and one SSRF policy (C3) - #38
Merged
Merged
Conversation
Every admitted caller is an administrator. api-config gains an auth-modes table (token, secretless loopback, password) with the session lifetime and the live-session setup and login cases, and one visibility table that the other pages link to. The observe-only redaction, private-path and local file path rules that contradicted it are removed.
Rename source db to override and state the effective value, null reset and override lifetime across restart and activation without honk's storage model. The URL count, interval bounds and checksum method are advertised in resources.geodata instead of fixed in the schema; honk's reconciliation and timing constants move to the honk notes. Both display URLs share one display form, and settings URLs are returned as written to every admitted caller.
Groups, node checks and geodata each carried their own copy of the SSRF rules, and the geodata copy required a resolved-address check that a download routed through a node cannot make. api-config now holds one policy with the direct, routed and member-dialled cases, and the three pages link to it.
Zakkaus
force-pushed
the
fix/contract-access-geodata
branch
from
September 28, 2026 17:11
b09e9c0 to
8a9b049
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Contract PR C3 of the API revision plan (access, geodata, SSRF). Plan decisions Q1 and Q8, plus the F12 and F43 paragraphs. Every changed rule was checked against honk
Glassyiris/honk@feat/native-apiand HB #59 (ae4453ce), and daeb59e375. dae has no native HTTP adapter or geodata downloader yet, so "dae" below means what a Go adapter needs to implement the text.F1: one visibility model (Q1)
api-config.md#Visibilityis the only visibility rule. Every admitted caller sees the same data whatever its permission, auth mode ordetailtier. Listener secrets are defined by role (each listener's deployment secret, the administrator password, session tokens; honk's arenative_api.secretandclash_api.secret). An engine masks the fields that carry them and the deployment secret values it holds; it need not keep a recoverable password or old tokens to find copies. Proxy and subscription credentials in node definitions, share links and provider URLs are returned as written. Parser and log output is sanitised, not forwarded. The leftover "never returned" and path-redaction rules are removed (api/providers.yaml,api/nodes-groups.yaml,api/config.yaml,api/discovery.yamlconfig.content,providers.md,node-latency.md,honk-mapping.mdProviders row);errors.mdlinks to the table. The remaining copies now refer to the table too: rule and DNS-rulesource.fileis returned as written andsourceis null only when unavailable (rules.md,dns-rules.md,api/rules.yaml); flow input fields follow the table and only the packet-body prohibition stays (flows.md); rule expressions are "display", not "sanitized display". Examples match: the provider list no longer says "without credentials", a created provider returns the URL it was created with, and rule/DNS examples show plain source paths.native_api/settings.rswith_geodata). The write rule (403 when not credentialed) stays.F24: auth modes and sessions
auth.mdandapi/auth.yamlorder the checks:Authorizationheader, peer (setup only), account state, body. A header without a live session gets 401. With a live session, setup gets 409setup_already_completed, and login still validates the body and opens another session only with correct credentials.api-config.mdkeeps session access and lifetime only. honk's 32-session limit and 12-hour lifetime move to a non-normativehonk-mapping.mdsection; they are examples, not requirements. Version and capabilities also require a bearer in password mode (api/discovery.yaml).security.rsboundaryauthenticates the header, thenauth/request.rschecks peer,setup_required(), and the credentials).F16: geodata lifecycle and bounds (Q8)
source: dbbecomesoverride.resources.geodata.lifecycleadvertisesfile_values(startoractivation) andoverrides_persist; honk's policy (start,true) is the documented default.geodata.mdhas a normative transition table: file field removed, same-value PATCH (staysoverride), omitted field,null, activation, restart without persistence. Patching one asset overrides only that list. Whenconfigurable_sourcesis true,max_urls,interval_hours {min,default,max},checksumandlifecycleare required, and min ≤ default ≤ max. JSON Schema checks presence andminimum: 1only; the ordering is a semantic constraint thattools/contract.mjsvalidateCapabilitieschecks, and the test runs it directly and shows the schema alone accepts inverted bounds.checksumis required whenevercan_updateis true;nullmeans unverified, andpinned(a digest the backend holds) is allowed.sha256sumappends.sha256sumto the path and keeps the query; a 404 means no checksum (accepted unverified), any other failure fails the URL. The schedule is anchored at the end of the last attempt; before this process finishes one, at process start or the latestauto_updatechange, whichever is later.next_check_atis set from startup while automatic updates are on; onlylast_checked_atandlast_updated_atstart null. The random delay is part of every wait, retries included.runtime-status.mdno longer fixes a geodata lifetime; it points to the advertised lifecycle and the transition table.geodata.md:41andapi/geodata.yaml:11wording fixed.honk-mapping.mddescribes honk as it is today (db, both lists stored on a one-asset patch, bounds enforced but not advertised).max_urls: 4,interval_hours {6,168,24},checksum: "sha256sum"andlifecycle {file_values: start, overrides_persist: true}(types.rs,geodata.rscapability). SerializeSource::Dbasoverride(geodata/sources.rs:151). InSources::apply, store only the patched URL list instead of freezing the sibling.common/subscriptionalready persists files), its own bounds, and validation before mutation. A bounded, no-redirect downloader;pinnedfitsscripts/fetch-geo-data.sh's digests.F12: display URLs
geodata::redact,looks_like_token): drop the query; redact the segment after a listed credential name, segments containing:or=, UUIDs, 32 hex digits, URL-safe segments of 16+ characters with upper, lower and digit, and URL-safe segments of 32+ characters without-that are not lower-case hex. Null when the URL does not parse, including a URL with userinfo or a fragment (HB'sparse_geodata_urlrejects both).feat/native-apistill keeps the query insource_redacted(geodata.rs:229).net/urlparse, reject userinfo and a non-emptyFragment, clearRawQuery, apply the listed segment tests.F43 and F11: one SSRF section
api-config.md#Outbound-requestsis the only copy. The restriction applies to check-execution requests; authorised writes (configuration sources, groupcheck_urlPATCH, geodata source PATCH) may change destinations, and the allowlists are deployment-owned. HTTP URL rules are separate from TCP/DNS host and port targets. Ports follow the destination kind: HTTP(S) takes the scheme default or an allowlisted port, TCP connect any nonzero configured node port, DNS 53 or an allowlisted port. A geodata URL equal, as the exact string, to the one the configuration file names for that asset is exempt from the address and port rules, checksum request included. Checks run after final route selection and before each dial and retry. When the backend resolves the name, direct or through a node, it validates and pins the address; when a node resolves it, only literals are checked; literals are always checked. Host and SNI keep the URL's name. The group health-check exemption covers both address and port rules, including direct members.groups.md,geodata.mdandcheck-nodes.mdlink to it.geodata.rsdownloadchecks ports, resolves and checks on direct, and checks literals on proxied routes;fetchskips the policy for the file URL and its checksum (geodata.rs:478,498-504,config/coordinator/geodata.rs:156-163);probes.rs:321-332has the per-kind ports; its group health checker applies no destination policy.control/dial.goafter route selection;dialer.goalready dials a locally selected IP, which is pinned after the check. The geodata exemption is a string comparison with the configured file URL.Scope notes
config.contentlines inconfiguration.md,api/config.yamlandapi/discovery.yaml. Whichever merges second needs a small rebase.Checks
npx -y yarn@1.22.22 check:contract: 74/74 pass (bundle regenerated and committed). The drift ledger records the file-URL exemption, per-kind ports and schedule anchor as matching honk, and the fragment rule under HB #59.