Skip to content

# NRP Remote Exchange Spec ​

## Purpose ​

This document defines the wire contract for remote NRP resolution over HTTP.

Goal:

- keep the exchange compatible with the current `cleaker` pointer runtime
- avoid coupling the kernel to HTTP route quirks
- leave room for future transport formats without changing `.me`

## Decision ​

Use plain JSON as the canonical exchange format for now.

Do not require JSON-LD yet.

Reason:

- current client code already consumes plain JSON
- the system still needs routing and normalization to stabilize
- JSON-LD can be added later as an alternate representation, not as the first contract

## Exchange Layers ​

### Semantic request ​

The client conceptually asks for:

- a canonical NRP target
- an operation kind
- optional authorization context

Example semantic target:

- `nrp://cleaker.me/users/ana/read/profile.name`

### Transport request ​

The HTTP transport maps that target into:

- method
- host
- path
- headers
- query

Example:

- `GET /profile/name`
- `Host: ana.cleaker.me`

## Canonical Operations ​

### `read` ​

Read a resolved semantic value from a remote namespace.

### `write` ​

Append a thought or memory event into a remote namespace.

### `claim` ​

Forge or reserve namespace identity.

### `open` ​

Verify the namespace trinity and recover replay state.

## Response Envelope ​

All remote HTTP responses should use a common envelope.

json

    {
      "ok": true,
      "operation": "read",
      "transport": {
        "protocol": "https",
        "method": "GET"
      },
      "target": {
        "nrp": "nrp://cleaker.me/users/ana/read/profile.name",
        "namespace": "cleaker.me/users/ana",
        "path": "profile.name"
      },
      "result": {
        "value": "Ana"
      },
      "meta": {
        "resolvedAt": 1773200000000
      }
    }

Rules:

- `ok` is always present
- `operation` is always present
- `target.namespace` is always canonical
- `target.nrp` is the semantic identity of the resolved request
- operation-specific payload goes inside `result`
- operational metadata goes inside `meta`

## Read Contract ​

### Request ​

Example:

http

    GET /profile/name HTTP/1.1
    Host: ana.cleaker.me
    Accept: application/json

### Successful response ​

json

    {
      "ok": true,
      "operation": "read",
      "target": {
        "nrp": "nrp://cleaker.me/users/ana/read/profile.name",
        "namespace": "cleaker.me/users/ana",
        "path": "profile.name"
      },
      "result": {
        "value": "Ana"
      },
      "meta": {
        "resolvedAt": 1773200000000,
        "namespaceRecordVerified": false
      }
    }

### Not found ​

json

    {
      "ok": false,
      "operation": "read",
      "target": {
        "nrp": "nrp://cleaker.me/users/ana/read/profile.unknown",
        "namespace": "cleaker.me/users/ana",
        "path": "profile.unknown"
      },
      "error": {
        "code": "PATH_NOT_FOUND",
        "message": "No value was resolved for the requested path."
      },
      "meta": {
        "resolvedAt": 1773200000000
      }
    }

## Write Contract ​

### Request ​

http

    POST / HTTP/1.1
    Host: ana.cleaker.me
    Content-Type: application/json

json

    {
      "identityHash": "claim-hash",
      "expression": "profile.name",
      "value": "Ana",
      "payload": {
        "path": "profile.name",
        "value": "Ana"
      }
    }

### Successful response ​

json

    {
      "ok": true,
      "operation": "write",
      "target": {
        "nrp": "nrp://cleaker.me/users/ana/write/profile.name",
        "namespace": "cleaker.me/users/ana",
        "path": "profile.name"
      },
      "result": {
        "blockId": "uuid",
        "timestamp": 1773200000000
      }
    }

## Claim Contract ​

### Request ​

json

    {
      "namespace": "ana.cleaker",
      "secret": "luna"
    }

### Successful response ​

json

    {
      "ok": true,
      "operation": "claim",
      "target": {
        "nrp": "nrp://cleaker.me/users/ana/claim",
        "namespace": "ana.cleaker"
      },
      "result": {
        "identityHash": "derived-hash"
      },
      "meta": {
        "createdAt": 1773200000000
      }
    }

## Open Contract ​

### Successful response ​

json

    {
      "ok": true,
      "operation": "open",
      "target": {
        "nrp": "nrp://cleaker.me/users/ana/open",
        "namespace": "ana.cleaker"
      },
      "result": {
        "identityHash": "derived-hash",
        "noise": "seeded-noise",
        "memories": [],
        "openedAt": 1773200000000
      },
      "meta": {
        "namespaceRecordVerified": true
      }
    }

## Error Contract ​

All failures should use this shape:

json

    {
      "ok": false,
      "operation": "read",
      "target": {
        "nrp": "nrp://cleaker.me/users/ana/read/profile.name",
        "namespace": "cleaker.me/users/ana",
        "path": "profile.name"
      },
      "error": {
        "code": "PATH_NOT_FOUND",
        "message": "No value was resolved for the requested path."
      },
      "meta": {
        "resolvedAt": 1773200000000
      }
    }

Rules:

- `error.code` is stable and machine-readable
- `error.message` is human-readable
- HTTP status and JSON error code must agree

Recommended mappings:

- `400` -> `BAD_REQUEST`
- `401` -> `UNAUTHORIZED`
- `403` -> `NAMESPACE_WRITE_FORBIDDEN`
- `404` -> `PATH_NOT_FOUND` or `CLAIM_NOT_FOUND`
- `409` -> `NAMESPACE_TAKEN`
- `422` -> `CLAIM_VERIFICATION_FAILED`
- `500` -> `INTERNAL_ERROR`

## Compatibility Rule ​

During migration, the server may continue returning the current minimal read shape:

json

    {
      "ok": true,
      "namespace": "cleaker.me/users/ana",
      "path": "profile.name",
      "value": "Ana"
    }

Client normalization should accept both:

- legacy minimal shape
- full response envelope

## Client Normalization ​

The `remotePointer` client should normalize read responses as follows:

1. If `result.value` exists, use that.
2. Else if top-level `value` exists, use that.
3. Else return the full payload as raw data.

This preserves compatibility with the current server while allowing a richer contract.

## Future Extension ​

After the envelope stabilizes, add optional:

- `@context`
- signed namespace records
- response signatures
- content negotiation for `application/ld+json`

But those should be additive, not required for the first remote contract.