diff --git a/README.md b/README.md index e339ada..d9ff087 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,195 @@ # schemata-validator-php [![Test](https://github.com/nostrability/schemata-validator-php/actions/workflows/test.yml/badge.svg)](https://github.com/nostrability/schemata-validator-php/actions/workflows/test.yml) +[![PHP 8.1+](https://img.shields.io/badge/php-8.1+-blue?style=flat-square)](https://php.net) +[![License](https://img.shields.io/badge/license-GPL--3.0--or--later-blue?style=flat-square)](LICENSE) -PHP validator for [Nostr](https://nostr.com/) JSON schemas. Uses [opis/json-schema](https://opis.io/json-schema/). +PHP validator for [Nostr](https://nostr.com/) protocol JSON schemas. Built on [`schemata-php`](https://github.com/nostrability/schemata-php) and [opis/json-schema](https://opis.io/json-schema/) (Draft 7). + +## Overview + +`schemata-validator-php` wraps the `schemata-php` embedded JSON Schema definitions with Opis validation, exposing ready-to-use static methods for common Nostr data structures. It validates Nostr events by kind and NIP-11 relay information documents. + +Validation results separate hard errors (schema violations) from soft warnings (missing schemas for unknown kinds). ## When to use this -JSON Schema validation is [not suited for runtime hot paths](https://github.com/nostrability/schemata#what-is-it-not-good-for). Use in **CI and integration tests**. + +JSON Schema validation is [not suited for runtime hot paths](https://github.com/nostrability/schemata#what-is-it-not-good-for). Use this in: + +- **CI pipelines** catching schema drift during builds +- **Integration tests** for clients and relays +- **PHPUnit suites** verifying event construction correctness + +## Installation + +Add to your `composer.json`: + +```json +{ + "require-dev": { + "nostrability/schemata-validator": "dev-main" + }, + "repositories": [ + { "type": "vcs", "url": "https://github.com/nostrability/schemata-validator-php" } + ] +} +``` + +Then run: + +```bash +composer install +``` + +The `schemata-php` data package is also required. Clone it as a sibling directory: + +```bash +git clone https://github.com/nostrability/schemata-php.git ../schemata-php +``` + +Requires PHP `>=8.1` and `opis/json-schema ^2.0`. + +## Quick Start + +```php +use Nostrability\SchemataValidator\SchemataValidator; + +$event = [ + 'id' => str_repeat('a', 64), + 'pubkey' => str_repeat('b', 64), + 'created_at' => 1700000000, + 'kind' => 1, + 'tags' => [], + 'content' => 'hello world', + 'sig' => str_repeat('c', 128), +]; + +$result = SchemataValidator::validateNote($event); +assert($result->valid, 'Errors: ' . print_r($result->errors, true)); +// $result->errors is empty, $result->warnings may flag unknown kinds +``` + +## API + +All methods are static on `SchemataValidator`. + +### `SchemataValidator::validate($schema, $data)` + +```php +public static function validate(array $schema, mixed $data): ValidationResult +``` + +Low-level validator. Compiles a JSON Schema with Opis and validates `$data` against it. Strips nested `$id`/`$schema` fields from the schema to prevent resolution issues. Use `validateNote` or `validateNip11` for common cases. + +| Parameter | Type | Description | +|-----------|------|-------------| +| `$schema` | `array` | A JSON Schema document as a PHP array | +| `$data` | `mixed` | The data to validate | + +### `SchemataValidator::validateNote($event)` + +```php +public static function validateNote(array $event): ValidationResult +``` + +Validates a Nostr event against the schema for its `kind`. The schema is looked up from `schemata-php` using the key `kind{N}Schema`. Returns a warning (not an error) if no schema exists for the given kind. + +| Parameter | Type | Description | +|-----------|------|-------------| +| `$event` | `array` | A Nostr event as an associative array | + +### `SchemataValidator::validateNip11($doc)` + +```php +public static function validateNip11(array $doc): ValidationResult +``` + +Validates a NIP-11 relay information document — the metadata object a relay serves at its HTTP endpoint — against the `nip11Schema`. + +| Parameter | Type | Description | +|-----------|------|-------------| +| `$doc` | `array` | A NIP-11 relay info document as an associative array | + +### `SchemataValidator::getSchema($key)` + +```php +public static function getSchema(string $key): ?array +``` + +Looks up a schema by key from the `schemata-php` registry. Returns `null` if the key doesn't exist. + +| Parameter | Type | Description | +|-----------|------|-------------| +| `$key` | `string` | Schema registry key (e.g., `"kind1Schema"`, `"pTagSchema"`) | + +### `ValidationResult` + +```php +class ValidationResult { + public bool $valid; + public array $errors; // ValidationError[] + public array $warnings; // ValidationError[] +} +``` + +- `$valid` — `true` if the data passes all schema constraints +- `$errors` — schema violations; empty when `$valid` is `true` +- `$warnings` — unknown kind / missing schema alerts + +### `ValidationError` + +```php +class ValidationError { + public string $instancePath; + public string $keyword; + public string $message; + public string $schemaPath; +} +``` + +## Usage Examples + +**Event validation:** + +```php +$result = SchemataValidator::validateNote([ + 'id' => str_repeat('a', 64), 'pubkey' => str_repeat('b', 64), + 'created_at' => 1700000000, 'kind' => 1, 'tags' => [], + 'content' => 'hello world', 'sig' => str_repeat('c', 128), +]); +assert($result->valid); +``` + +**NIP-11 validation:** + +```php +$result = SchemataValidator::validateNip11([ + 'name' => 'My Relay', + 'supported_nips' => [1, 11], +]); +assert($result->valid); +``` + +**Direct schema lookup:** + +```php +$schema = SchemataValidator::getSchema('kind1Schema'); +assert($schema !== null); +``` + +## Known Limitations + +- **Partial kind coverage:** Only event kinds with a corresponding schema in `@nostrability/schemata` can be validated. `validateNote` returns a warning (not an error) when no schema exists for the given kind. +- **No `validateMessage`:** Protocol message validation is not yet implemented in this package. +- **No recursive content validation:** The `content` field of events containing stringified JSON (e.g., kind 0 metadata) is not recursively validated. +- **Alpha accuracy:** False positives and negatives are possible. The underlying schemas are in active development. + +## Related Packages + +- [`schemata-php`](https://github.com/nostrability/schemata-php) — PHP data package containing embedded schemas and registry +- [`@nostrability/schemata`](https://github.com/nostrability/schemata) — canonical language-agnostic schema definitions +- [`@nostrwatch/schemata-js-ajv`](https://github.com/sandwichfarm/nostr-watch/tree/next/libraries/schemata-js-ajv) — JavaScript/TypeScript validator implementation ## License -GPL-3.0-or-later + +[GPL-3.0-or-later](LICENSE)