fed-sx-types Phase 3: /types/<cid> route + discovery_type_fetch
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 51s

Wire format for serving + fetching type docs (plans/fed-sx-host-types.md
step 3).

http_server.erl:
- new type_doc Accept format + content type
  (application/vnd.fed-sx.type-doc), distinct from actor-doc.
- GET /types/<cid> -> the cached TypeRecord term_codec-encoded, 404 if
  not in the peer_types cache. Reads peer_types via a Cfg
  {peer_types, peer_types} guard (hardcoded registered atom, mirroring
  the actor-doc route's kernel guard).

discovery_type_fetch.erl — sibling of discovery_fetch. make_fetch_fn
produces the fun/2 peer_types:lookup_or_fetch calls: GET
<base>/types/<cid> with the type-doc Accept header, returning the RAW
bytes (peer_types owns the term_codec decode, so the wire format lives
in one place — the route encodes, the cache decodes). Cfg carries
type_url / type_url_fn for TypeCid -> base URL resolution.

Tests: next/tests/peer_types_route.sh (13, in-process route dispatch),
next/tests/discovery_type_fetch.sh (9, closure vs a python type-doc
stub, end-to-end through peer_types:lookup_or_fetch).

No regression: http_accept, http_actors, http_get_format,
discovery_fetch all still green. Conformance 771/771.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-30 15:48:33 +00:00
parent 8d54028c7f
commit 441a895737
4 changed files with 498 additions and 1 deletions

View File

@@ -4,6 +4,7 @@
welcome_body/0, capabilities_body/0,
capabilities_path/0,
match_prefix/2, actors_prefix/0, actor_doc_response/1,
types_prefix/0, type_doc_response_for/2,
artifacts_prefix/0, artifact_response/1,
projections_list_path/0, projections_prefix/0,
projections_list_response/0, projection_response/1,
@@ -156,7 +157,12 @@ dispatch(<<71, 69, 84>>, Path, F, Cfg) ->
{ok, Name} when byte_size(Name) > 0 ->
projection_response_for(Name, F);
_ ->
not_found_response()
case match_prefix(types_prefix(), Path) of
{ok, Cid} when byte_size(Cid) > 0 ->
type_doc_response_for(Cid, Cfg);
_ ->
not_found_response()
end
end
end
end;
@@ -289,6 +295,10 @@ artifact_response(Cid) ->
Body = <<Pre/binary, Cid/binary, 10>>,
ok_response(Body).
%% "/types/" — 7 bytes: 47 116 121 112 101 115 47 (host-type fed Step 3)
types_prefix() ->
<<47,116,121,112,101,115,47>>.
%% "/projections" — 12 bytes (no trailing slash; the list endpoint)
projections_list_path() ->
<<47,112,114,111,106,101,99,116,105,111,110,115>>.
@@ -488,9 +498,20 @@ actor_doc_prefix() ->
118,110,100,46,102,101,100,45,115,120,46,
97,99,116,111,114,45,100,111,99>>.
%% "application/vnd.fed-sx.type-doc" — 31 bytes (host-type fed Step 3).
%% Distinct from actor-doc: the body is a term_codec-encoded
%% TypeRecord (peer_types cache entry), not a peer-actor-state.
type_doc_prefix() ->
<<97,112,112,108,105,99,97,116,105,111,110,47,
118,110,100,46,102,101,100,45,115,120,46,
116,121,112,101,45,100,111,99>>.
accept_format(nil) -> text;
accept_format(<<>>) -> text;
accept_format(V) when is_binary(V) ->
case match_prefix(type_doc_prefix(), V) of
{ok, _} -> type_doc;
_ ->
case match_prefix(actor_doc_prefix(), V) of
{ok, _} -> actor_doc;
_ ->
@@ -510,6 +531,7 @@ accept_format(V) when is_binary(V) ->
end
end
end
end
end;
accept_format(_) -> text.
@@ -586,6 +608,11 @@ content_type_for(actor_doc) ->
<<97,112,112,108,105,99,97,116,105,111,110,47,
118,110,100,46,102,101,100,45,115,120,46,
97,99,116,111,114,45,100,111,99>>;
%% "application/vnd.fed-sx.type-doc" — 31 bytes (host-type fed Step 3).
content_type_for(type_doc) ->
<<97,112,112,108,105,99,97,116,105,111,110,47,
118,110,100,46,102,101,100,45,115,120,46,
116,121,112,101,45,100,111,99>>;
content_type_for(_) ->
content_type_for(text).
@@ -714,6 +741,42 @@ kernel_actor_state(_Kernel, Id) ->
_ -> nil
end.
%% ── host-type fed Step 3: GET /types/<cid> ──────────────────────
%%
%% Serves a TypeRecord the node has cached (its own published types or
%% types fetched from peers) so a federated peer running
%% discovery_type_fetch can decode it directly into the shape
%% peer_types + the object-schema pipeline stage consume. The wire
%% body is term_codec:encode(TypeRecord) under the
%% application/vnd.fed-sx.type-doc content type; a cache miss is a 404.
%%
%% Cid is the path segment after "/types/" (the type's CID bytes). Cfg
%% carries `{peer_types, peer_types}` to opt the route into the cache —
%% absent (or the gen_server down) short-circuits to 404, matching the
%% kernel_actor_state guard for the actor-doc route. This port can't
%% dispatch `Mod:Fun` on a variable module, so the registered
%% `peer_types` atom is hardcoded; the Cfg field flags "no cache wired".
type_doc_response_for(Cid, Cfg) ->
case type_record_for(Cfg, Cid) of
nil -> not_found_response();
TR -> ok_response(term_codec:encode(TR), type_doc)
end.
type_record_for(Cfg, Cid) ->
case field(peer_types, Cfg) of
nil -> nil;
_ ->
case erlang:whereis(peer_types) of
undefined -> nil;
_ ->
case peer_types:lookup(Cid) of
{ok, TR} -> TR;
_ -> nil
end
end
end.
%% ── Step 4a: per-actor sub-resource stubs ──────────────────────
%% Per design §16.1 each actor has /outbox /inbox /followers
%% /following routes. v1 returns text-stub bodies so route resolution