diff --git a/hubzilla/addon/vs01/vs01_spool.php b/hubzilla/addon/vs01/vs01_spool.php
index 877eb9d..5a50b2a 100644
--- a/hubzilla/addon/vs01/vs01_spool.php
+++ b/hubzilla/addon/vs01/vs01_spool.php
@@ -7,9 +7,9 @@
* See VS-RENDERER-SPEC.md — Spool POST Handler section.
*/
-// ----------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
// POST HANDLER
-// ----------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
function vs01_handle_post($association_slug, $vs_code, $access) {
$schemas = vs01_load_schemas();
@@ -62,30 +62,29 @@ function vs01_handle_post($association_slug, $vs_code, $access) {
$out .= '
';
}
-// ----------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
// VALIDATION
-// ----------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
function vs01_validate_required($perspective, $fields) {
$errors = [];
@@ -116,32 +115,65 @@ function vs01_validate_required($perspective, $fields) {
return $errors;
}
-// ----------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
// SPOOL ENVELOPE
-// ----------------------------------------------------------------------------
+// Matches contracts/vs01/record-v1.json — _header / _payload shape.
+// ---------------------------------------------------------------------------
-function vs01_build_spool_envelope($vs_code, $perspective, $fields, $association_slug, $channel_id) {
+function vs01_build_spool_envelope($vs_code, $perspective, $fields, $association_slug, $channel_id, $standing, $node_token) {
return [
- 'addon' => 'vs01',
- 'contract_version' => '1.0',
- 'vs_code' => $vs_code,
- 'perspective' => $perspective,
- 'association_slug' => $association_slug,
- 'association_channel_id' => (string) $channel_id,
- 'submitted_at' => date('c'),
- 'standing' => vs01_access_state($association_slug),
- 'fields' => $fields,
+ '_header' => [
+ 'addon' => 'vs01',
+ 'contract_version' => '1.0',
+ 'association_slug' => $association_slug,
+ 'association_channel_id' => (string) $channel_id,
+ 'participant_id' => vs01_get_participant_id($association_slug),
+ 'submitted_at' => date('c'),
+ 'standing' => $standing,
+ 'node_token_hash' => hash('sha256', $node_token),
+ ],
+ '_payload' => [
+ 'vs_code' => $vs_code,
+ 'perspective' => $perspective,
+ 'fields' => $fields,
+ ],
];
}
-// ----------------------------------------------------------------------------
-// ORCHESTRATOR POST
-// ----------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
+// PARTICIPANT ID — Placekey derived from SASE channel address
+// ---------------------------------------------------------------------------
-function vs01_post_to_orchestrator($envelope) {
- $config = vs01_load_config();
+function vs01_get_participant_id($association_slug) {
+ // The participant_id is the Placekey — the SASE-verified channel address
+ // for this participant, which encodes their postal address.
+ // It is stored in the channel's xchan_addr field.
+ // Format: SASE1-{encoded-address}
+ $observer = get_observer_hash();
+ if (!$observer) return '';
+
+ $r = q("SELECT xchan_addr FROM xchan WHERE xchan_hash = '%s' LIMIT 1",
+ dbesc($observer)
+ );
+ if (!$r) return '';
+
+ // xchan_addr is in the form "channelname@hostname"
+ // The channel name IS the Placekey for SASE participants.
+ $addr = $r[0]['xchan_addr'] ?? '';
+ $parts = explode('@', $addr);
+ return $parts[0] ?? '';
+}
+
+// ---------------------------------------------------------------------------
+// ORCHESTRATOR POST
+// ---------------------------------------------------------------------------
+
+function vs01_post_to_orchestrator($envelope, $config = null) {
+ if ($config === null) {
+ $config = vs01_load_config();
+ }
$receiver_url = $config['receiver_url'] ?? '';
- $node_token = $config['node_token'] ?? '';
+ $node_token = $config['node_token'] ?? '';
if (!$receiver_url) {
logger('vs01_spool: receiver_url not configured');
@@ -166,8 +198,8 @@ function vs01_post_to_orchestrator($envelope) {
],
]);
- $response = curl_exec($ch);
- $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ $response = curl_exec($ch);
+ $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
curl_close($ch);
@@ -184,9 +216,9 @@ function vs01_post_to_orchestrator($envelope) {
return 'Orchestrator error (HTTP ' . $http_code . ').';
}
-// ----------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
// RE-RENDER FORM WITH SUBMITTED VALUES
-// ----------------------------------------------------------------------------
+// ---------------------------------------------------------------------------
function vs01_render_vs_form_with_values($association_slug, $vs_code, $access, $submitted_values) {
$schemas = vs01_load_schemas();