Fundamentals
PHP Data Placement
Install the PHP 8.2+ SDK and enable the cURL extension for the control-plane helper:
composer require neutron-ai/sdk
The official SDK handles runtime memory operations. The helper below calls the public Nucleus REST endpoints for provisioning and readiness checks.
Provision the three Nuclei
<?php
declare(strict_types=1);
function requiredEnv(string $name): string
{
$value = getenv($name);
if ($value === false || trim($value) === '') {
throw new RuntimeException("{$name} is required");
}
return $value;
}
function neutronRequest(
string $token,
string $method,
string $path,
?array $body = null,
): array {
$base_url = rtrim(requiredEnv('NEUTRON_API_URL'), '/');
$curl = curl_init($base_url . $path);
if ($curl === false) {
throw new RuntimeException('Unable to initialize cURL');
}
curl_setopt_array($curl, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => [
'authorization: Bearer ' . $token,
'content-type: application/json',
],
CURLOPT_POSTFIELDS => $body === null ? null : json_encode($body, JSON_THROW_ON_ERROR),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$raw_body = curl_exec($curl);
$status = (int) curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$error = curl_error($curl);
curl_close($curl);
if ($raw_body === false) {
throw new RuntimeException($error ?: 'Neutron request failed');
}
$decoded = $raw_body === '' ? [] : json_decode($raw_body, true, flags: JSON_THROW_ON_ERROR);
if ($status >= 400) {
throw new RuntimeException("Neutron request failed with HTTP {$status}");
}
return is_array($decoded) ? $decoded : [];
}
$profiles = [
[
'nucleusId' => 'product-knowledge-global',
'name' => 'Global product knowledge',
'placement' => [
'mode' => 'global',
'guarantee' => 'none',
'notes' => ['Approved global knowledge; no jurisdictional storage guarantee.'],
],
],
[
'nucleusId' => 'support-operations-weur',
'name' => 'Western Europe support operations',
'placement' => [
'mode' => 'regional',
'regionHint' => 'weur',
'r2LocationHint' => 'weur',
'guarantee' => 'best_effort_regional',
'notes' => ['Latency preference only; not a legal residency boundary.'],
],
],
[
'nucleusId' => 'regulated-cases-eu',
'name' => 'EU regulated case memory',
'placement' => [
'mode' => 'jurisdictional',
'jurisdiction' => 'eu',
'regionalServicesRequired' => true,
'metadataBoundaryRequired' => true,
'geoKeyManagerRequired' => false,
'guarantee' => 'jurisdictional_storage',
'notes' => [
'Requires the EU Durable Object jurisdiction and matching EU archive binding.',
'Processing, logs, metadata, keys, retention, and contracts require separate verification.',
],
],
],
];
$workspace_key = requiredEnv('NEUTRON_WORKSPACE_API_KEY');
foreach ($profiles as $profile) {
neutronRequest($workspace_key, 'POST', '/v1/nuclei', $profile);
}
Treat an already-existing Nucleus as deployment state. Never retry creation with a different placement profile.
Build the memory router
<?php
use NeutronAI\NeutronAIClient;
$base_url = requiredEnv('NEUTRON_API_URL');
$global_memory = new NeutronAIClient(
base_url: $base_url,
token: requiredEnv('NEUTRON_GLOBAL_TOKEN'),
nucleus_id: 'product-knowledge-global',
);
$western_europe_memory = new NeutronAIClient(
base_url: $base_url,
token: requiredEnv('NEUTRON_WEUR_TOKEN'),
nucleus_id: 'support-operations-weur',
);
$eu_regulated_memory = new NeutronAIClient(
base_url: $base_url,
token: requiredEnv('NEUTRON_EU_TOKEN'),
nucleus_id: 'regulated-cases-eu',
);
function storeGlobalProductKnowledge(NeutronAIClient $client, string $text): array
{
return $client->remember([
'scopeId' => 'kb:approved-products',
'type' => 'knowledge',
'privacyClass' => 'public',
'text' => $text,
]);
}
function storeWesternEuropeSupportLesson(NeutronAIClient $client, string $text): array
{
return $client->remember([
'scopeId' => 'history:support-resolutions',
'type' => 'tool_lesson',
'privacyClass' => 'tenant',
'text' => $text,
]);
}
function storeEuRegulatedCase(
NeutronAIClient $client,
string $case_id,
string $minimized_summary,
): array {
return $client->remember([
'scopeId' => "case:{$case_id}",
'type' => 'experience',
'privacyClass' => 'user_private',
'text' => $minimized_summary,
'metadata' => [
'dataClass' => 'eu-regulated-case',
'source' => 'approved-case-summary',
],
]);
}
Expose only these domain-specific operations to application code. Do not accept placement or unrestricted scope values from an agent, browser, or request body.
Recall EU case context
$eu_case_context = $eu_regulated_memory->agentContext([
'scopeIds' => [
'case:case-4821',
'policy:eu-case-handling',
'kb:approved-products',
],
'agentId' => 'agent:eu-case-support',
'task' => 'Prepare the next authorized case-support step.',
'privacyClasses' => ['tenant', 'user_private'],
'tokenBudget' => 1_200,
'cachePolicy' => [
'mode' => 'prefer_cache',
'keyMode' => 'intent_profile',
'ttlSeconds' => 120,
'includeDynamicRag' => true,
'allowSensitive' => false,
],
]);
All requested scopes resolve inside regulated-cases-eu; the call cannot cross into another Nucleus.
Verify jurisdictional readiness
$eu_token = requiredEnv('NEUTRON_EU_TOKEN');
$placement = neutronRequest(
$eu_token,
'GET',
'/v1/nuclei/regulated-cases-eu/placement',
);
$health = neutronRequest(
$eu_token,
'GET',
'/v1/nuclei/regulated-cases-eu/health',
);
$blocking_warnings = [
'jurisdictional_do_subnamespace_unavailable',
'jurisdictional_archive_bucket_missing',
];
$active_warnings = $health['warnings'] ?? [];
if (array_intersect($blocking_warnings, $active_warnings) !== []) {
throw new RuntimeException('EU Nucleus is not ready');
}
Return to the placement architecture and production checklist.