# Palace Beacon developer guide

Developer preview. The profile gateway runs locally; public account and key provisioning is still being built.

## Installation prompt

Integrate Palace Beacon into this app. First read https://palacebeacon.com/developers/guide.md and https://palacebeacon.com/developers/openapi.json, including linked schemas.

Inspect the framework, backend, authentication, agent lifecycle, consent controls and ad surfaces. Beacon learns interests from user statements/actions; ads belong in separate, visibly labeled placements. Preserve the user's task and normal agent replies.

Use the server-only adapter at https://palacebeacon.com/developers/sdk/agent.mjs (with its taxonomy.json dependency) or implement the documented HTTP contract. Read BEACON_API_ORIGIN and BEACON_API_KEY from backend secrets. Never put keys, profile IDs or grant authority in browser bundles or model prompts. Bind appSubjectId and grantId through the trusted authenticated session; never accept them from model output.

Search the eligible taxonomy. Extract only supported user interests, retaining origin, confidence, expiry and an opaque evidence reference. Reject third-person, hypothetical, agent-authored and unsupported sensitive-trait claims. Submit stable event IDs through the host's durable job queue, outside the response path. Do not send raw conversations.

Read purpose-scoped profiles server-side: ads for a separate ad placement; personalization only with that permission. Treat an empty profile as normal. Keep app users isolated unless a trusted provisioner establishes an authorized shared profile. Implement retraction, revocation and expiry. Keep the app usable when Beacon is unavailable.

Optionally integrate location.mjs: trusted IP data yields campus area candidates; explicit device permission plus accuracy can yield a campus estimate. Never infer employment or enrollment, and never link users by IP or location.

This is a developer preview, not a provisioned public service. If credentials or access to the local gateway are missing, scaffold the adapter behind a disabled feature flag, test with clearly labeled synthetic fixtures, and report the exact missing configuration. Do not invent an npm package, public API host, API key, consent grant, paid inventory or successful live installation.

Show the files changed and demonstrate: a supported event, duplicate retry, authorized profile read, unlinked isolation, retraction, revocation, and unavailable-service behavior. Keep signal hooks separate from the ad placement. Report local proof separately from production readiness.

## Install Beacon

Copy the installation prompt above into the coding agent working in your application. It describes the signal hooks, permission bindings and separate ad placement to implement.

For the examples below, save the server adapter, taxonomy.json and client.mjs in the same backend directory. Set BEACON_API_ORIGIN, BEACON_API_KEY and BEACON_SUBJECT from your local provisioner. The subject must match a grant for this app; in production, resolve it from the authenticated session.

```javascript
import { BeaconAgentClient } from './agent.mjs';

export function required(name) {
  const value = process.env[name];
  if (!value) throw new Error('Set ' + name + ' on the backend.');
  return value;
}

export const subject = required('BEACON_SUBJECT');
export const beacon = new BeaconAgentClient({
  origin: required('BEACON_API_ORIGIN'),
  apiKey: required('BEACON_API_KEY'),
});
```

- [Download server adapter](https://palacebeacon.com/developers/sdk/agent.mjs)
- [Download taxonomy.json](https://palacebeacon.com/developers/sdk/taxonomy.json)
- [Download client.mjs](https://palacebeacon.com/developers/examples/client.mjs)

## Run the local gateway



1. Use a checkout of the Beacon repository. The gateway is a local development service.
2. Run the integration check, build the profile process, then start the gateway with a private configuration and state path.
3. In the configuration, register an agent-enabled app and a random server key of at least 32 characters. Bind its subject and profile in an AgentGrant with the required purposes. The fixture provisioner supplies these bindings; it does not verify production account ownership.

```sh
npm run agent:check
cargo build --locked --offline --bin beacon-profiles
node ops/agent-gateway.mjs /private/config.json /private/profiles.json
```

The gateway listens on 127.0.0.1:8958. Keep its configuration and state outside public assets.

- [App, grant and event contract](https://palacebeacon.com/developers/interests.schema.json)

## Record an interest event

Submit evidence from a user statement or action through the host’s durable backend queue. Return the agent’s response without waiting for Beacon.

This local fixture records tennis affinity. Set BEACON_EVENT_ID to a stable ID and BEACON_GRANT_ID to its provisioned grant. Use the same event ID when retrying; changing content under that ID is rejected.

```javascript
import { beacon, subject, required } from './client.mjs';

const receipt = await beacon.track({
  schemaVersion: 1,
  id: required('BEACON_EVENT_ID'),
  grantId: required('BEACON_GRANT_ID'),
  appSubjectId: subject,
  conversationId: 'local-tennis-example',
  topicId: 'sports.tennis',
  kind: 'affinity',
  origin: 'user_statement',
  about: 'user',
  confidence: 0.9,
  observedAtMs: Date.now(),
  expiresAtMs: Date.now() + 30 * 24 * 60 * 60 * 1000,
  evidenceRef: 'local-example-turn-1',
});
console.log(JSON.stringify(receipt, null, 2));
```

In a host integration, bind identity and grant in trusted code. Replace the fixture conversation and evidence reference with opaque host references. Send structured evidence rather than conversation text.

A statement about another person, a hypothetical question or agent-authored text cannot establish a user interest. An ad response retains its own origin and lower weighting.

- [Event schema](https://palacebeacon.com/developers/interests.schema.json)
- [Download record-interest.mjs](https://palacebeacon.com/developers/examples/record-interest.mjs)

## Find an interest category

Search eligible category labels, IDs and source paths. Use the returned ID in an event.

The catalog has 1,013 IDs, with 998 eligible for profiles. It includes 154 Google affinity and 779 in-market source paths. Catalog coverage does not measure recognition accuracy; imported entries have no automatic keyword classifier. Authenticated Meta IDs are not available.

Affinity lasts at most 90 days and intent 14 days. Scores decay with half-lives of 21 and 3 days. Confidence is a host assertion rather than a calibrated probability.

## Read an interest profile

Fetch an ads profile from your backend when choosing an ad for a separate placement.

```javascript
import { beacon, subject } from './client.mjs';

const profile = await beacon.profile(subject, 'ads');
console.log(JSON.stringify(profile, null, 2));
```

```json
{
  "taxonomyVersion": "beacon-interests-3",
  "asOfMs": 1794002400000,
  "purpose": "ads",
  "topics": [
    {
      "topicId": "sports.tennis",
      "label": "Tennis",
      "kind": "affinity",
      "score": 0.9,
      "evidenceCount": 1,
      "supportingApps": 1,
      "lastObservedAtMs": 1794002400000,
      "expiresAtMs": 1796594400000
    }
  ]
}
```

- ads: Requires the grant’s ads purpose; use the topics in a separate placement.
- personalization: Requires the separate personalization purpose.
- Cross-app evidence: Requires a trusted shared identity and network permission on both source and receiving grants.

A successful empty profile has topics: []. Use your contextual fallback when there is no permitted, unexpired evidence.

A missing or expired subject grant is a domain rejection (400). An invalid app key returns 401. Keep the host usable if the request fails.

- [Download read-profile.mjs](https://palacebeacon.com/developers/examples/read-profile.mjs)
- [Profile contract](https://palacebeacon.com/developers/interests.schema.json)
- [Publisher placement example](https://palacebeacon.com/examples/publisher)

## Retract evidence or revoke a grant

Use the backend client and app subject from Installation. Each operation below is a separate action.

Set BEACON_EVENT_ID to the source event to remove. Its tombstone prevents a retry from restoring the evidence.

```javascript
import { beacon, subject, required } from './client.mjs';

const receipt = await beacon.retract(subject, required('BEACON_EVENT_ID'));
console.log(JSON.stringify(receipt, null, 2));
```

Set BEACON_GRANT_ID to the app grant to withdraw. Revocation erases its source events and survives restart.

```javascript
import { beacon, subject, required } from './client.mjs';

const receipt = await beacon.revoke(subject, required('BEACON_GRANT_ID'));
console.log(JSON.stringify(receipt, null, 2));
```

## Match a campus location

The matcher includes Samsung Digital City in Suwon and Stanford’s main campus. An IP point yields area candidates. A permissioned device estimate can yield a location match when its reported accuracy circle plus a 25 m guard fits inside the mapped perimeter.

IP accuracy is unknown here. Finer provider coordinates are used internally for candidate matching; public coordinates and weather cache cells remain coarse. Stale, denied and missing-accuracy readings stay unavailable. Boundary overlaps remain candidates.

Browser Geolocation requires an explicit user action and permission. The demo processes coordinates locally without uploading or retaining them. A campus estimate does not establish employment or enrollment. The community-mapped perimeters are not surveyed boundaries.

Network registration is separate: AS32 maps to Stanford University and AS6619 to Samsung SDS, including possible customer or remote traffic. These signals do not establish a person’s affiliation.

- [Location module](https://palacebeacon.com/developers/sdk/location.mjs)
- [Required campus registry](https://palacebeacon.com/developers/sdk/campuses.json)
- [Location schema](https://palacebeacon.com/developers/location.schema.json)
- [© OpenStreetMap contributors · ODbL](https://www.openstreetmap.org/copyright)

## API reference

Use a backend bearer key for every /v1 request. The key determines the app. POST bodies must be JSON and at most 64 KiB. The adapter requires HTTPS outside loopback and has a five-second timeout. /api/context is the website’s separate, unauthenticated environment endpoint.

Gateway success responses use HTTP 200. Errors contain an error string: 400 for invalid input or a domain rejection, 401 for an invalid key, 404 for an unknown route, 413 for an oversized body, 415 for a non-JSON POST, and 429 at local capacity. Queue transient retries with the same event ID; invalid grants require correction.

## Verify the integration



1. Record one supported interest and confirm it appears in a permitted profile. Replay the same event without increasing its count.
2. Read another, unlinked user’s profile and confirm it contains no shared evidence. Reject hypothetical, other-person and agent-authored claims.
3. Retract the event, then verify it disappears and cannot return through replay. Test grant revocation independently, including a process restart.
4. Delay or fail the Beacon request while continuing the host conversation and navigation. Prove durable replay after a host restart. Keep credentials out of bundles, telemetry and model context.

The local engine is single-writer and acknowledges atomic private-file replacement. Its limit is 100,000 events plus tombstones. Abrupt termination can require stale-lock recovery after confirming the writer is gone. Production provisioning, retention compaction, extraction evaluation and paid delivery remain implementation work.

## API contract

https://palacebeacon.com/developers/openapi.json
