diff --git a/composer.json b/composer.json
index 5ff740424120d80d1c145908228e4358513a54c4..a6ce25841eb10ed6bb4aa7fbda1378c68fba89e2 100644
--- a/composer.json
+++ b/composer.json
@@ -29,7 +29,6 @@
         "league/commonmark": "^2.4",
         "league/oauth2-server": "^8.5",
         "nette/php-generator": "^4.1",
-        "nette/schema": "^1.2",
         "nicmart/tree": "^0.7.2",
         "nyholm/psr7": "^1.8",
         "nyholm/psr7-server": "^1.1",
@@ -48,7 +47,7 @@
         "webonyx/graphql-php": "^15.6",
         "dragonmantank/cron-expression": "^3.3",
         "league/oauth2-client": "^2.7",
-        "justinrainbow/json-schema": "^5.2"
+        "opis/json-schema": "^2.3"
     },
     "require-dev": {
         "mockery/mockery": "^1.6",
diff --git a/composer.lock b/composer.lock
index 3c0993b2bd0f174bdc277f8b5a2a404bd2bfa42b..44312f18cfd99ab8f65cf6aae01fc7608ceffb66 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "593dce046df08176585b42c57e05193e",
+    "content-hash": "54c7a151e1eb3558a606574811103c43",
     "packages": [
         {
             "name": "defuse/php-encryption",
@@ -1780,76 +1780,6 @@
             ],
             "time": "2023-12-03T20:05:35+00:00"
         },
-        {
-            "name": "justinrainbow/json-schema",
-            "version": "v5.2.13",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/justinrainbow/json-schema.git",
-                "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/fbbe7e5d79f618997bc3332a6f49246036c45793",
-                "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793",
-                "shasum": ""
-            },
-            "require": {
-                "php": ">=5.3.3"
-            },
-            "require-dev": {
-                "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1",
-                "json-schema/json-schema-test-suite": "1.2.0",
-                "phpunit/phpunit": "^4.8.35"
-            },
-            "bin": [
-                "bin/validate-json"
-            ],
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "5.0.x-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "JsonSchema\\": "src/JsonSchema/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Bruno Prieto Reis",
-                    "email": "bruno.p.reis@gmail.com"
-                },
-                {
-                    "name": "Justin Rainbow",
-                    "email": "justin.rainbow@gmail.com"
-                },
-                {
-                    "name": "Igor Wiedler",
-                    "email": "igor@wiedler.ch"
-                },
-                {
-                    "name": "Robert Schönthal",
-                    "email": "seroscho@googlemail.com"
-                }
-            ],
-            "description": "A library to validate a json schema.",
-            "homepage": "https://github.com/justinrainbow/json-schema",
-            "keywords": [
-                "json",
-                "schema"
-            ],
-            "support": {
-                "issues": "https://github.com/justinrainbow/json-schema/issues",
-                "source": "https://github.com/justinrainbow/json-schema/tree/v5.2.13"
-            },
-            "time": "2023-09-26T02:20:38+00:00"
-        },
         {
             "name": "lcobucci/clock",
             "version": "3.2.0",
@@ -2976,6 +2906,196 @@
             ],
             "time": "2023-11-08T09:30:43+00:00"
         },
+        {
+            "name": "opis/json-schema",
+            "version": "2.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/opis/json-schema.git",
+                "reference": "c48df6d7089a45f01e1c82432348f2d5976f9bfb"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/opis/json-schema/zipball/c48df6d7089a45f01e1c82432348f2d5976f9bfb",
+                "reference": "c48df6d7089a45f01e1c82432348f2d5976f9bfb",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "opis/string": "^2.0",
+                "opis/uri": "^1.0",
+                "php": "^7.4 || ^8.0"
+            },
+            "require-dev": {
+                "ext-bcmath": "*",
+                "ext-intl": "*",
+                "phpunit/phpunit": "^9.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Opis\\JsonSchema\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "Sorin Sarca",
+                    "email": "sarca_sorin@hotmail.com"
+                },
+                {
+                    "name": "Marius Sarca",
+                    "email": "marius.sarca@gmail.com"
+                }
+            ],
+            "description": "Json Schema Validator for PHP",
+            "homepage": "https://opis.io/json-schema",
+            "keywords": [
+                "json",
+                "json-schema",
+                "schema",
+                "validation",
+                "validator"
+            ],
+            "support": {
+                "issues": "https://github.com/opis/json-schema/issues",
+                "source": "https://github.com/opis/json-schema/tree/2.3.0"
+            },
+            "time": "2022-01-08T20:38:03+00:00"
+        },
+        {
+            "name": "opis/string",
+            "version": "2.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/opis/string.git",
+                "reference": "9ebf1a1f873f502f6859d11210b25a4bf5d141e7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/opis/string/zipball/9ebf1a1f873f502f6859d11210b25a4bf5d141e7",
+                "reference": "9ebf1a1f873f502f6859d11210b25a4bf5d141e7",
+                "shasum": ""
+            },
+            "require": {
+                "ext-iconv": "*",
+                "ext-json": "*",
+                "php": "^7.4 || ^8.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Opis\\String\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "Marius Sarca",
+                    "email": "marius.sarca@gmail.com"
+                },
+                {
+                    "name": "Sorin Sarca",
+                    "email": "sarca_sorin@hotmail.com"
+                }
+            ],
+            "description": "Multibyte strings as objects",
+            "homepage": "https://opis.io/string",
+            "keywords": [
+                "multi-byte",
+                "opis",
+                "string",
+                "string manipulation",
+                "utf-8"
+            ],
+            "support": {
+                "issues": "https://github.com/opis/string/issues",
+                "source": "https://github.com/opis/string/tree/2.0.1"
+            },
+            "time": "2022-01-14T15:42:23+00:00"
+        },
+        {
+            "name": "opis/uri",
+            "version": "1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/opis/uri.git",
+                "reference": "0f3ca49ab1a5e4a6681c286e0b2cc081b93a7d5a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/opis/uri/zipball/0f3ca49ab1a5e4a6681c286e0b2cc081b93a7d5a",
+                "reference": "0f3ca49ab1a5e4a6681c286e0b2cc081b93a7d5a",
+                "shasum": ""
+            },
+            "require": {
+                "opis/string": "^2.0",
+                "php": "^7.4 || ^8.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Opis\\Uri\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "Marius Sarca",
+                    "email": "marius.sarca@gmail.com"
+                },
+                {
+                    "name": "Sorin Sarca",
+                    "email": "sarca_sorin@hotmail.com"
+                }
+            ],
+            "description": "Build, parse and validate URIs and URI-templates",
+            "homepage": "https://opis.io",
+            "keywords": [
+                "URI Template",
+                "parse url",
+                "punycode",
+                "uri",
+                "uri components",
+                "url",
+                "validate uri"
+            ],
+            "support": {
+                "issues": "https://github.com/opis/uri/issues",
+                "source": "https://github.com/opis/uri/tree/1.1.0"
+            },
+            "time": "2021-05-22T15:57:08+00:00"
+        },
         {
             "name": "paragonie/random_compat",
             "version": "v9.99.100",
diff --git a/config.ini.example b/config.ini.example
index 2494f64135e913871deef53ccf5b1fc2f49a5396..8c4c6783cc3c5652108f1338fe8531c50850a020 100644
--- a/config.ini.example
+++ b/config.ini.example
@@ -29,9 +29,6 @@ default[timeout] = 1
 default[pool_prefill] = false
 default[pool_size] = 8
 
-[sentry]
-dsn =
-
 [session]
 cookie_lifespan = 86400
 cookie_name = dmsession
diff --git a/docs/pages/docs/features/configuration/index.md b/docs/pages/docs/features/configuration/index.md
index 2acaaa0f663e13c61d6a53c32f52f3084125e6b6..a3b7d1108c22adb67fbbffa6df13596a720638c7 100644
--- a/docs/pages/docs/features/configuration/index.md
+++ b/docs/pages/docs/features/configuration/index.md
@@ -160,7 +160,7 @@ readonly class ManifestConfiguration
 ```
 
 Then you need to define the configuration provider. 
-[nette/schema](https://doc.nette.org/en/schema) is used for config validation:
+[JSON Schema](https://json-schema.org/) is used for config validation:
 
 ```php
 <?php
@@ -169,9 +169,8 @@ namespace App\SingletonProvider\ConfigurationProvider;
 
 use App\ManifestConfiguration;
 use Distantmagic\Resonance\Attribute\Singleton;
+use Distantmagic\Resonance\JsonSchema;
 use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
-use Nette\Schema\Expect;
-use Nette\Schema\Schema;
 
 /**
  * @template-extends ConfigurationProvider<ManifestConfiguration, object{
@@ -187,11 +186,21 @@ final readonly class ManifestConfigurationProvider extends ConfigurationProvider
         return 'manifest';
     }
 
-    protected function getSchema(): Schema
+    protected function getSchema(): JsonSchema
     {
-        return Expect::structure([
-            'background_color' => Expect::string()->min(1)->required(),
-            'theme_color' => Expect::string()->min(1)->required(),
+        return new JsonSchema([
+            'type' => 'object',
+            'properties' => [
+                'background_color' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+                'theme_color' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ]
+            ],
+            'required' => ['background_color', 'theme_color']
         ]);
     }
 
diff --git a/docs/pages/docs/features/validation/index.md b/docs/pages/docs/features/validation/index.md
index 240401389383e6b61922b8c925ff358c027b2020..f6dc6f989c9c5c0f613bd9058dd75b32d17245ff 100644
--- a/docs/pages/docs/features/validation/index.md
+++ b/docs/pages/docs/features/validation/index.md
@@ -48,9 +48,7 @@ readonly class BlogPostForm extends InputValidatedData
 
 Validators take in any data and check if it adheres to the configuration 
 schema. The `makeSchema()` method must return a 
-[nette/schema](https://doc.nette.org/en/schema) object. You can follow
-their documentation to learn how to create a validation schema (in concept,
-it's similar to the [json-schema](https://json-schema.org)).
+[JSON Schema](https://json-schema.org/) object.
 
 ```php
 <?php
@@ -60,12 +58,11 @@ namespace App\InputValidator;
 use App\InputValidatedData\BlogPostForm;
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\InputValidator;
+use Distantmagic\Resonance\JsonSchema;
 use Distantmagic\Resonance\SingletonCollection;
-use Nette\Schema\Expect;
-use Nette\Schema\Schema;
 
 /**
- * @extends InputValidator<BlogPostForm, object{
+ * @extends InputValidator<BlogPostForm, array{
  *     content: string,
  *     title: string,
  * }>
@@ -76,16 +73,26 @@ readonly class BlogPostFormValidator extends InputValidator
     protected function castValidatedData(mixed $data): BlogPostForm
     {
         return new BlogPostForm(
-            $data->content,
-            $data->title,
+            $data['content'],
+            $data['title'],
         );
     }
 
     protected function makeSchema(): Schema
     {
-        return Expect::structure([
-            'content' => Expect::string()->min(1)->required(),
-            'title' => Expect::string()->min(1)->required(),
+        return new JsonSchema([
+            'type' => 'object',
+            'properties' => [
+                'content' => [
+                    'type' => 'string',
+                    'minLength' => 1
+                ],
+                'title' => [
+                    'type' => 'string',
+                    'minLength' => 1
+                ]
+            ],
+            'required' => ['content', 'title']
         ]);
     }
 }
diff --git a/docs/pages/index.md b/docs/pages/index.md
index a46e1ffc158c4160d86c8dfd5bdb673b3e96f679..804ee2d7a4226cbdbcffff05b4c04184d264322e 100644
--- a/docs/pages/index.md
+++ b/docs/pages/index.md
@@ -141,24 +141,14 @@ readonly class Homepage implements HttpResponderInterface
                         class="language-php"
                         data-controller="hljs"
                         data-hljs-language-value="php"
-                    >use function Sentry\init as sentryInit;
-
+                    >
 #[ListensTo(HttpServerStarted::class)]
 #[Singleton(collection: SingletonCollection::EventListener)]
-final readonly class InitializeSentry extends EventListener
+final readonly class InitializeErrorReporting extends EventListener
 {
-    public function __construct(
-        private ApplicationConfiguration $applicationContext,
-        private SentryConfiguration $sentryConfiguration,
-    ) {}
-
     public function handle(EventInterface $event): void
     {
-        sentryInit([
-            'default_integrations' => false,
-            'dsn' => $this->sentryConfiguration->dsn,
-            'environment' => $this->applicationContext->environment->value,
-        ]);
+        // ...
     }
 }
 </code></pre>
diff --git a/docs/pages/tutorials/session-based-authentication/index.md b/docs/pages/tutorials/session-based-authentication/index.md
index b4c883d038a275c7add3ae4374f27a09199a6cdd..cf61a5cca292bed622701cee9787f8a1fdaf2faf 100644
--- a/docs/pages/tutorials/session-based-authentication/index.md
+++ b/docs/pages/tutorials/session-based-authentication/index.md
@@ -342,7 +342,7 @@ readonly class UsernamePassword extends InputValidatedData
 ```
 
 We will use this input validator in the HTTP Responder. It uses
-[Nette's Data Validator](https://doc.nette.org/en/schema)
+[JSON Schema](https://json-schema.org/)
 to validate the incoming data:
 
 ```php file:app/InputValidator/UsernamePasswordValidator.php
@@ -353,9 +353,8 @@ namespace App\InputValidator;
 use App\InputValidatedData\UsernamePassword;
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\InputValidator;
+use Distantmagic\Resonance\JsonSchema;
 use Distantmagic\Resonance\SingletonCollection;
-use Nette\Schema\Expect;
-use Nette\Schema\Schema;
 
 /**
  * @extends InputValidator<UsernamePassword, object{
@@ -374,10 +373,22 @@ readonly class UsernamePasswordValidator extends InputValidator
 
     protected function makeSchema(): Schema
     {
-        return Expect::structure([
-            'csrf' => Expect::string()->required(),
-            'username' => Expect::string()->min(1)->required(),
-            'password' => Expect::string()->min(1)->required(),
+        return new JsonSchema([
+            'type' => 'object',
+            'properties' => [
+                'csrf' => [
+                    'type' => 'string',
+                ],
+                'username' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+                'password' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ]
+            ],
+            'required' => ['csrf', 'username', 'password']
         ]);
     }
 }
diff --git a/resources/css/docs-page-homepage.css b/resources/css/docs-page-homepage.css
index 15738789558b07081f3ca6cfa0ab8a87f43698a8..99ad27395049fe94794fe0cc1ead2ae283ca49d5 100644
--- a/resources/css/docs-page-homepage.css
+++ b/resources/css/docs-page-homepage.css
@@ -114,12 +114,10 @@ h2.homepage__example__title {
 }
 
 .homepage__title h1 {
-  font-family: var(--font-family-lora);
   line-height: 1;
 
   @media screen and (max-width: 1023px) {
     font-size: 3.5em;
-    letter-spacing: -5px;
   }
   @media screen and (min-width: 1024px) {
     font-size: 6em;
diff --git a/src/Command/Watch.php b/src/Command/Watch.php
index f0c5c4e0a9c2789f51b306f89038b095c3260015..fce35a4bab429f3da03d53f00fe6a60cb4822e84 100644
--- a/src/Command/Watch.php
+++ b/src/Command/Watch.php
@@ -79,16 +79,19 @@ final class Watch extends Command
             $this->process = null;
         }
 
-        $this->process = new Process(static function (Process $worker) use ($childCommandName) {
-            /**
-             * @psalm-suppress InvalidArgument false positive
-             * @psalm-suppress InvalidCast false positive
-             */
-            $worker->exec(PHP_BINARY, [
-                realpath(DM_APP_ROOT.'/../bin/resonance.php'),
-                $childCommandName,
-            ]);
-        });
+        $this->process = new Process(
+            callback: static function (Process $worker) use ($childCommandName) {
+                /**
+                 * @psalm-suppress InvalidArgument false positive
+                 * @psalm-suppress InvalidCast false positive
+                 */
+                $worker->exec(PHP_BINARY, [
+                    realpath(DM_APP_ROOT.'/../bin/resonance.php'),
+                    $childCommandName,
+                ]);
+            },
+            redirect_stdin_and_stdout: false,
+        );
 
         $this->process->start();
         $this->process->wait(false);
diff --git a/src/ExpectUrl.php b/src/ExpectUrl.php
deleted file mode 100644
index 99e5e2da6a55b5b2d462093e79c99b53dba46942..0000000000000000000000000000000000000000
--- a/src/ExpectUrl.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Distantmagic\Resonance;
-
-readonly class ExpectUrl
-{
-    public function __invoke(string $pattern): bool
-    {
-        // if user puts here something like gopher://abc it's on them
-        return false !== filter_var($pattern, FILTER_VALIDATE_URL);
-    }
-}
diff --git a/src/InputValidator.php b/src/InputValidator.php
index bc8b4889e07cdcfe43fa903327ddf9fb7cfe0ffa..5ce18e3e0829a9ba14307239c89425721ee9ac56 100644
--- a/src/InputValidator.php
+++ b/src/InputValidator.php
@@ -37,8 +37,9 @@ abstract readonly class InputValidator
          * @var JsonSchemaValidationResult<TValidatedData>
          */
         $jsonSchemaValidationResult = $this->jsonSchemaValidator->validate($this->jsonSchema, $data);
+        $errors = $jsonSchemaValidationResult->errors;
 
-        if ($jsonSchemaValidationResult->validator->isValid()) {
+        if (empty($errors)) {
             return new InputValidationResult($this->castValidatedData($jsonSchemaValidationResult->data));
         }
 
@@ -47,16 +48,10 @@ abstract readonly class InputValidator
          */
         $validationResult = new InputValidationResult();
 
-        /**
-         * @var array{
-         *   property: string,
-         *   message: string
-         * } $error
-         */
-        foreach ($jsonSchemaValidationResult->validator->getErrors() as $error) {
+        foreach ($errors as $propertyName => $propertyErrors) {
             $validationResult->errors->put(
-                $error['property'],
-                $error['message'],
+                $propertyName,
+                implode("\n", $propertyErrors),
             );
         }
 
diff --git a/src/JsonSchemaValidationResult.php b/src/JsonSchemaValidationResult.php
index 50e52c8d3b57911afd08ce05819a81b2b54975c5..0120fd6e7189c032698636586dd95bd2e6cda265 100644
--- a/src/JsonSchemaValidationResult.php
+++ b/src/JsonSchemaValidationResult.php
@@ -4,18 +4,17 @@ declare(strict_types=1);
 
 namespace Distantmagic\Resonance;
 
-use JsonSchema\Validator;
-
 /**
  * @template TValidatedData
  */
 readonly class JsonSchemaValidationResult
 {
     /**
-     * @param TValidatedData $data
+     * @param TValidatedData              $data
+     * @param array<string,array<string>> $errors
      */
     public function __construct(
-        public Validator $validator,
         public mixed $data,
+        public array $errors,
     ) {}
 }
diff --git a/src/JsonSchemaValidator.php b/src/JsonSchemaValidator.php
index b9b21a93b9a46a913d2c846498ae59df46232c00..ed0a080d49768e2fb2fa2f3f28f3961665db950b 100644
--- a/src/JsonSchemaValidator.php
+++ b/src/JsonSchemaValidator.php
@@ -5,34 +5,57 @@ declare(strict_types=1);
 namespace Distantmagic\Resonance;
 
 use Distantmagic\Resonance\Attribute\Singleton;
-use JsonSchema\Constraints\Constraint;
-use JsonSchema\Constraints\Factory;
-use JsonSchema\Validator;
+use Opis\JsonSchema\Errors\ErrorFormatter;
+use Opis\JsonSchema\Helper;
+use Opis\JsonSchema\ValidationResult;
+use Opis\JsonSchema\Validator;
 
 #[Singleton]
 readonly class JsonSchemaValidator
 {
-    private Factory $factory;
+    private ErrorFormatter $errorFormatter;
+    private Validator $validator;
 
-    public function __construct(ApplicationConfiguration $applicationConfiguration)
+    public function __construct()
     {
-        $productionFlags = Constraint::CHECK_MODE_TYPE_CAST | Constraint::CHECK_MODE_APPLY_DEFAULTS;
-
-        $this->factory = new Factory(
-            checkMode: Environment::Development === $applicationConfiguration->environment
-                ? $productionFlags | Constraint::CHECK_MODE_VALIDATE_SCHEMA
-                : $productionFlags,
-        );
+        $this->errorFormatter = new ErrorFormatter();
+        $this->validator = new Validator();
     }
 
     public function validate(JsonSchema $jsonSchema, mixed $data): JsonSchemaValidationResult
     {
-        $validator = new Validator($this->factory);
-        $validator->validate($data, $jsonSchema->schema);
+        /**
+         * @var bool|object|string $convertedSchema
+         */
+        $convertedSchema = Helper::toJSON($jsonSchema->schema);
+
+        /**
+         * @var bool|object|string $convertedData
+         */
+        $convertedData = Helper::toJSON($data);
+
+        $validationResult = $this->validator->validate($convertedData, $convertedSchema);
 
         return new JsonSchemaValidationResult(
-            validator: $validator,
-            data: $data,
+            data: $convertedData,
+            errors: $this->formatErrors($validationResult),
         );
     }
+
+    /**
+     * @return array<string,array<string>>
+     */
+    private function formatErrors(ValidationResult $validationResult): array
+    {
+        $error = $validationResult->error();
+
+        if (empty($error)) {
+            return [];
+        }
+
+        /**
+         * @var array<string,array<string>>
+         */
+        return $this->errorFormatter->formatKeyed($error);
+    }
 }
diff --git a/src/SingletonProvider/ConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider.php
index 0ed4bd4bca9f7fb8b3cf6549e8367dd476bd46db..6a2fc300da1d4f718c3bef4f9dd9139babcf5b89 100644
--- a/src/SingletonProvider/ConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider.php
@@ -5,11 +5,13 @@ declare(strict_types=1);
 namespace Distantmagic\Resonance\SingletonProvider;
 
 use Distantmagic\Resonance\ConfigurationFile;
+use Distantmagic\Resonance\JsonSchema;
+use Distantmagic\Resonance\JsonSchemaValidationResult;
+use Distantmagic\Resonance\JsonSchemaValidator;
 use Distantmagic\Resonance\PHPProjectFiles;
 use Distantmagic\Resonance\SingletonContainer;
 use Distantmagic\Resonance\SingletonProvider;
-use Nette\Schema\Processor;
-use Nette\Schema\Schema;
+use LogicException;
 
 /**
  * @template TObject of object
@@ -19,9 +21,11 @@ use Nette\Schema\Schema;
  */
 abstract readonly class ConfigurationProvider extends SingletonProvider
 {
+    private JsonSchema $jsonSchema;
+
     abstract protected function getConfigurationKey(): string;
 
-    abstract protected function getSchema(): Schema;
+    abstract protected function makeSchema(): JsonSchema;
 
     /**
      * @param TSchema $validatedData
@@ -32,8 +36,10 @@ abstract readonly class ConfigurationProvider extends SingletonProvider
 
     public function __construct(
         private ConfigurationFile $configurationFile,
-        private Processor $processor,
-    ) {}
+        private JsonSchemaValidator $jsonSchemaValidator,
+    ) {
+        $this->jsonSchema = $this->makeSchema();
+    }
 
     /**
      * @return TObject
@@ -41,14 +47,33 @@ abstract readonly class ConfigurationProvider extends SingletonProvider
     public function provide(SingletonContainer $singletons, PHPProjectFiles $phpProjectFiles): object
     {
         /**
-         * @var TSchema $validatedData
+         * @var mixed $data explicitly mixed for typechecks
+         */
+        $data = $this->configurationFile->config->get($this->getConfigurationKey());
+
+        /**
+         * @var JsonSchemaValidationResult<TSchema>
          */
-        $validatedData = $this->processor->process(
-            $this->getSchema(),
-            $this->configurationFile->config->get($this->getConfigurationKey()),
-        );
+        $jsonSchemaValidationResult = $this->jsonSchemaValidator->validate($this->jsonSchema, $data);
+
+        $errors = $jsonSchemaValidationResult->errors;
+
+        if (empty($errors)) {
+            return $this->provideConfiguration($jsonSchemaValidationResult->data);
+        }
+
+        $messages = [];
+
+        foreach ($errors as $propertyName => $propertyErrors) {
+            foreach ($propertyErrors as $propertyError) {
+                $messages[] = sprintf('"%s": "%s"', $propertyName, $propertyError);
+            }
+        }
 
-        return $this->provideConfiguration($validatedData);
+        throw new LogicException(sprintf(
+            "Encountered validation errors:\n-> %s",
+            implode("\n-> ", $messages)
+        ));
     }
 
     public function shouldRegister(): bool
diff --git a/src/SingletonProvider/ConfigurationProvider/ApplicationConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/ApplicationConfigurationProvider.php
index f8e850f96527d6d438b9ab11fbbc537d063364c9..2ce54d5a49ea2883d15ab3e584e65fdfa2286372 100644
--- a/src/SingletonProvider/ConfigurationProvider/ApplicationConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/ApplicationConfigurationProvider.php
@@ -7,10 +7,8 @@ namespace Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 use Distantmagic\Resonance\ApplicationConfiguration;
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\Environment;
-use Distantmagic\Resonance\ExpectUrl;
+use Distantmagic\Resonance\JsonSchema;
 use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
-use Nette\Schema\Expect;
-use Nette\Schema\Schema;
 
 /**
  * @template-extends ConfigurationProvider<ApplicationConfiguration, object{
@@ -28,13 +26,32 @@ final readonly class ApplicationConfigurationProvider extends ConfigurationProvi
         return 'app';
     }
 
-    protected function getSchema(): Schema
+    protected function makeSchema(): JsonSchema
     {
-        return Expect::structure([
-            'env' => Expect::anyOf(...Environment::values())->required(),
-            'esbuild_metafile' => Expect::string()->min(1)->default('esbuild-meta.json'),
-            'scheme' => Expect::anyOf('http', 'https')->default('https'),
-            'url' => Expect::string()->required()->assert(new ExpectUrl()),
+        return new JsonSchema([
+            'type' => 'object',
+            'properties' => [
+                'env' => [
+                    'type' => 'string',
+                    'enum' => Environment::values(),
+                ],
+                'esbuild_metafile' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                    'default' => 'esbuild-meta.json',
+                ],
+                'scheme' => [
+                    'type' => 'string',
+                    'enum' => ['http', 'https'],
+                    'default' => 'https',
+                ],
+                'url' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                    'format' => 'uri',
+                ],
+            ],
+            'required' => ['env', 'url'],
         ]);
     }
 
diff --git a/src/SingletonProvider/ConfigurationProvider/DatabaseConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/DatabaseConfigurationProvider.php
index abbed870b45af0404aeee8cf3f6360467c40d3cc..b04319d15fdce8e47fc631426e097564127fbff8 100644
--- a/src/SingletonProvider/ConfigurationProvider/DatabaseConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/DatabaseConfigurationProvider.php
@@ -8,9 +8,8 @@ use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\DatabaseConfiguration;
 use Distantmagic\Resonance\DatabaseConnectionPoolConfiguration;
 use Distantmagic\Resonance\DatabaseConnectionPoolDriverName;
+use Distantmagic\Resonance\JsonSchema;
 use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
-use Nette\Schema\Expect;
-use Nette\Schema\Schema;
 
 /**
  * @template-extends ConfigurationProvider<
@@ -37,24 +36,67 @@ final readonly class DatabaseConfigurationProvider extends ConfigurationProvider
         return 'database';
     }
 
-    protected function getSchema(): Schema
+    protected function makeSchema(): JsonSchema
     {
-        $keySchema = Expect::string()->min(1)->required();
+        $valueSchema = [
+            'type' => 'object',
+            'properties' => [
+                'database' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+                'driver' => [
+                    'type' => 'string',
+                    'enum' => DatabaseConnectionPoolDriverName::values(),
+                ],
+                'host' => [
+                    'type' => ['string', 'null'],
+                    'minLength' => 1,
+                    'default' => null,
+                ],
+                'log_queries' => [
+                    'type' => 'boolean',
+                ],
+                'password' => [
+                    'type' => 'string',
+                ],
+                'pool_prefill' => [
+                    'type' => 'boolean',
+                ],
+                'pool_size' => [
+                    'type' => 'integer',
+                    'minimum' => 1,
+                ],
+                'port' => [
+                    'type' => 'integer',
+                    'minimum' => 1,
+                    'maximum' => 65535,
+                    'default' => 3306,
+                ],
+                'unix_socket' => [
+                    'type' => ['string', 'null'],
+                    'default' => null,
+                ],
+                'username' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+            ],
+            'required' => [
+                'database',
+                'driver',
+                'log_queries',
+                'password',
+                'pool_prefill',
+                'pool_size',
+                'username',
+            ],
+        ];
 
-        $valueSchema = Expect::structure([
-            'database' => Expect::string()->min(1)->required(),
-            'driver' => Expect::anyOf(...DatabaseConnectionPoolDriverName::values())->required(),
-            'host' => Expect::string()->min(1)->nullable()->default(null),
-            'log_queries' => Expect::bool()->required(),
-            'password' => Expect::string()->required(),
-            'pool_prefill' => Expect::bool()->required(),
-            'pool_size' => Expect::int()->min(1)->required(),
-            'port' => Expect::int()->min(1)->max(65535)->default(3306),
-            'unix_socket' => Expect::string()->nullable()->default(null),
-            'username' => Expect::string()->min(1)->required(),
+        return new JsonSchema([
+            'type' => 'object',
+            'additionalProperties' => $valueSchema,
         ]);
-
-        return Expect::arrayOf($valueSchema, $keySchema);
     }
 
     protected function provideConfiguration($validatedData): DatabaseConfiguration
diff --git a/src/SingletonProvider/ConfigurationProvider/OAuth2ConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/OAuth2ConfigurationProvider.php
index 1a7b16ca0fa92b77fcf15cec8d5ba63a2994a76e..58fab3f8a6128f37c09fec642114600c83af025d 100644
--- a/src/SingletonProvider/ConfigurationProvider/OAuth2ConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/OAuth2ConfigurationProvider.php
@@ -7,11 +7,10 @@ namespace Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 use Defuse\Crypto\Key;
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\Feature;
+use Distantmagic\Resonance\JsonSchema;
 use Distantmagic\Resonance\OAuth2Configuration;
 use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 use League\OAuth2\Server\CryptKey;
-use Nette\Schema\Expect;
-use Nette\Schema\Schema;
 use RuntimeException;
 use Swoole\Coroutine;
 
@@ -37,16 +36,44 @@ final readonly class OAuth2ConfigurationProvider extends ConfigurationProvider
         return 'oauth2';
     }
 
-    protected function getSchema(): Schema
+    protected function makeSchema(): JsonSchema
     {
-        return Expect::structure([
-            'encryption_key' => Expect::string()->min(1)->required(),
-            'jwt_signing_key_passphrase' => Expect::string()->default(null),
-            'jwt_signing_key_private' => Expect::string()->min(1)->required(),
-            'jwt_signing_key_public' => Expect::string()->min(1)->required(),
-            'session_key_authorization_request' => Expect::string()->min(1)->default('oauth2.authorization_request'),
-            'session_key_pkce' => Expect::string()->min(1)->default('oauth2.pkce'),
-            'session_key_state' => Expect::string()->min(1)->default('oauth2.state'),
+        return new JsonSchema([
+            'type' => 'object',
+            'properties' => [
+                'encryption_key' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+                'jwt_signing_key_passphrase' => [
+                    'type' => 'string',
+                    'default' => null,
+                ],
+                'jwt_signing_key_private' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+                'jwt_signing_key_public' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+                'session_key_authorization_request' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                    'default' => 'oauth2.authorization_request',
+                ],
+                'session_key_pkce' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                    'default' => 'oauth2.pkce',
+                ],
+                'session_key_state' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                    'default' => 'oauth2.state',
+                ],
+            ],
+            'required' => ['encryption_key', 'jwt_signing_key_private', 'jwt_signing_key_public'],
         ]);
     }
 
diff --git a/src/SingletonProvider/ConfigurationProvider/OpenAPIConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/OpenAPIConfigurationProvider.php
index cb67d42f2da082ca8c21b0ff83b4e4d8c0bb2a0d..3014813cd2292b79205a9f518e34d1657960318f 100644
--- a/src/SingletonProvider/ConfigurationProvider/OpenAPIConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/OpenAPIConfigurationProvider.php
@@ -5,10 +5,9 @@ declare(strict_types=1);
 namespace Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 
 use Distantmagic\Resonance\Attribute\Singleton;
+use Distantmagic\Resonance\JsonSchema;
 use Distantmagic\Resonance\OpenAPIConfiguration;
 use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
-use Nette\Schema\Expect;
-use Nette\Schema\Schema;
 
 /**
  * @template-extends ConfigurationProvider<OpenAPIConfiguration, object{
@@ -25,12 +24,25 @@ final readonly class OpenAPIConfigurationProvider extends ConfigurationProvider
         return 'openapi';
     }
 
-    protected function getSchema(): Schema
+    protected function makeSchema(): JsonSchema
     {
-        return Expect::structure([
-            'description' => Expect::string()->min(1)->required(),
-            'title' => Expect::string()->min(1)->required(),
-            'version' => Expect::string()->min(1)->required(),
+        return new JsonSchema([
+            'type' => 'object',
+            'properties' => [
+                'description' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+                'title' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+                'version' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+            ],
+            'required' => ['description', 'title', 'version'],
         ]);
     }
 
diff --git a/src/SingletonProvider/ConfigurationProvider/RedisConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/RedisConfigurationProvider.php
index 950377e237fabd559d3ad6e2813cd676c06feb71..3f8c1e7aa56bbff7d8620dd62ddb206fa04906c4 100644
--- a/src/SingletonProvider/ConfigurationProvider/RedisConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/RedisConfigurationProvider.php
@@ -5,11 +5,10 @@ declare(strict_types=1);
 namespace Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 
 use Distantmagic\Resonance\Attribute\Singleton;
+use Distantmagic\Resonance\JsonSchema;
 use Distantmagic\Resonance\RedisConfiguration;
 use Distantmagic\Resonance\RedisConnectionPoolConfiguration;
 use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
-use Nette\Schema\Expect;
-use Nette\Schema\Schema;
 
 /**
  * @template-extends ConfigurationProvider<
@@ -34,22 +33,59 @@ final readonly class RedisConfigurationProvider extends ConfigurationProvider
         return 'redis';
     }
 
-    protected function getSchema(): Schema
+    protected function makeSchema(): JsonSchema
     {
-        $keySchema = Expect::string()->min(1)->required();
+        $valueSchema = [
+            'type' => 'object',
+            'properties' => [
+                'db_index' => [
+                    'type' => 'integer',
+                    'minimum' => 0,
+                ],
+                'host' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+                'password' => [
+                    'type' => 'string',
+                ],
+                'pool_prefill' => [
+                    'type' => 'boolean',
+                ],
+                'pool_size' => [
+                    'type' => 'integer',
+                    'minimum' => 1,
+                ],
+                'port' => [
+                    'type' => 'integer',
+                    'minimum' => 1,
+                    'maximum' => 65535,
+                ],
+                'prefix' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+                'timeout' => [
+                    'type' => 'integer',
+                    'minimum' => 0,
+                ],
+            ],
+            'required' => [
+                'db_index',
+                'host',
+                'password',
+                'pool_prefill',
+                'pool_size',
+                'port',
+                'prefix',
+                'timeout',
+            ],
+        ];
 
-        $valueSchema = Expect::structure([
-            'db_index' => Expect::int()->min(0)->required(),
-            'host' => Expect::string()->min(1)->required(),
-            'password' => Expect::string()->required(),
-            'pool_prefill' => Expect::bool()->required(),
-            'pool_size' => Expect::int()->min(1)->required(),
-            'port' => Expect::int()->min(1)->max(65535)->required(),
-            'prefix' => Expect::string()->min(1)->required(),
-            'timeout' => Expect::int()->min(0)->required(),
+        return new JsonSchema([
+            'type' => 'object',
+            'additionalProperties' => $valueSchema,
         ]);
-
-        return Expect::arrayOf($valueSchema, $keySchema);
     }
 
     protected function provideConfiguration($validatedData): RedisConfiguration
diff --git a/src/SingletonProvider/ConfigurationProvider/SessionConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/SessionConfigurationProvider.php
index 2d173940018607aece1ea97f7c968636bb90fe8f..665109350e0340b875aa11ab6fc01ea530a802f0 100644
--- a/src/SingletonProvider/ConfigurationProvider/SessionConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/SessionConfigurationProvider.php
@@ -6,12 +6,11 @@ namespace Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 
 use Distantmagic\Resonance\Attribute\Singleton;
 use Distantmagic\Resonance\ConfigurationFile;
+use Distantmagic\Resonance\JsonSchema;
+use Distantmagic\Resonance\JsonSchemaValidator;
 use Distantmagic\Resonance\RedisConfiguration;
 use Distantmagic\Resonance\SessionConfiguration;
 use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
-use Nette\Schema\Expect;
-use Nette\Schema\Processor;
-use Nette\Schema\Schema;
 
 /**
  * @template-extends ConfigurationProvider<SessionConfiguration, object{
@@ -26,10 +25,10 @@ final readonly class SessionConfigurationProvider extends ConfigurationProvider
 {
     public function __construct(
         private ConfigurationFile $configurationFile,
-        private Processor $processor,
+        private JsonSchemaValidator $jsonSchemaValidator,
         private RedisConfiguration $redisConfiguration,
     ) {
-        parent::__construct($configurationFile, $processor);
+        parent::__construct($configurationFile, $jsonSchemaValidator);
     }
 
     protected function getConfigurationKey(): string
@@ -37,7 +36,7 @@ final readonly class SessionConfigurationProvider extends ConfigurationProvider
         return 'session';
     }
 
-    protected function getSchema(): Schema
+    protected function makeSchema(): JsonSchema
     {
         $redisConnectionPools = $this
             ->redisConfiguration
@@ -46,11 +45,28 @@ final readonly class SessionConfigurationProvider extends ConfigurationProvider
             ->toArray()
         ;
 
-        return Expect::structure([
-            'cookie_lifespan' => Expect::int()->min(1)->required(),
-            'cookie_name' => Expect::string()->min(1)->required(),
-            'cookie_samesite' => Expect::anyOf('lax', 'none', 'strict')->default('lax'),
-            'redis_connection_pool' => Expect::anyOf(...$redisConnectionPools),
+        return new JsonSchema([
+            'type' => 'object',
+            'properties' => [
+                'cookie_lifespan' => [
+                    'type' => 'integer',
+                    'minimum' => 1,
+                ],
+                'cookie_name' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+                'cookie_samesite' => [
+                    'type' => 'string',
+                    'enum' => ['lax', 'none', 'strict'],
+                    'default' => 'lax',
+                ],
+                'redis_connection_pool' => [
+                    'type' => 'string',
+                    'enum' => $redisConnectionPools,
+                ],
+            ],
+            'required' => ['cookie_lifespan', 'cookie_name'],
         ]);
     }
 
diff --git a/src/SingletonProvider/ConfigurationProvider/StaticPageConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/StaticPageConfigurationProvider.php
index b041f33cfe431893deb811c7db7078eb1a9d9229..b5f87b6943e2d9e8936972fde8414a0f4847290c 100644
--- a/src/SingletonProvider/ConfigurationProvider/StaticPageConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/StaticPageConfigurationProvider.php
@@ -5,10 +5,9 @@ declare(strict_types=1);
 namespace Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 
 use Distantmagic\Resonance\Attribute\Singleton;
+use Distantmagic\Resonance\JsonSchema;
 use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 use Distantmagic\Resonance\StaticPageConfiguration;
-use Nette\Schema\Expect;
-use Nette\Schema\Schema;
 
 /**
  * @template-extends ConfigurationProvider<StaticPageConfiguration, object{
@@ -27,14 +26,38 @@ final readonly class StaticPageConfigurationProvider extends ConfigurationProvid
         return 'static';
     }
 
-    protected function getSchema(): Schema
+    protected function makeSchema(): JsonSchema
     {
-        return Expect::structure([
-            'base_url' => Expect::string()->min(1)->required(),
-            'esbuild_metafile' => Expect::string()->min(1)->required(),
-            'input_directory' => Expect::string()->min(1)->required(),
-            'output_directory' => Expect::string()->min(1)->required(),
-            'sitemap' => Expect::string()->min(1)->required(),
+        return new JsonSchema([
+            'type' => 'object',
+            'properties' => [
+                'base_url' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                    'required' => true,
+                ],
+                'esbuild_metafile' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                    'required' => true,
+                ],
+                'input_directory' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                    'required' => true,
+                ],
+                'output_directory' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                    'required' => true,
+                ],
+                'sitemap' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                    'required' => true,
+                ],
+            ],
+            'required' => ['base_url', 'esbuild_metafile', 'input_directory', 'output_directory', 'sitemap'],
         ]);
     }
 
diff --git a/src/SingletonProvider/ConfigurationProvider/SwooleConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/SwooleConfigurationProvider.php
index ca82c0b586fba42ddd4ef4cf9f6f7110c1cff2c9..ade316341eaf772262b4d7415b7c8ce46a346ebe 100644
--- a/src/SingletonProvider/ConfigurationProvider/SwooleConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/SwooleConfigurationProvider.php
@@ -5,10 +5,9 @@ declare(strict_types=1);
 namespace Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 
 use Distantmagic\Resonance\Attribute\Singleton;
+use Distantmagic\Resonance\JsonSchema;
 use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 use Distantmagic\Resonance\SwooleConfiguration;
-use Nette\Schema\Expect;
-use Nette\Schema\Schema;
 
 /**
  * @template-extends ConfigurationProvider<SwooleConfiguration, object{
@@ -28,15 +27,38 @@ final readonly class SwooleConfigurationProvider extends ConfigurationProvider
         return 'swoole';
     }
 
-    protected function getSchema(): Schema
+    protected function makeSchema(): JsonSchema
     {
-        return Expect::structure([
-            'host' => Expect::string()->min(1)->required(),
-            'log_level' => Expect::string()->min(1)->required(),
-            'log_requests' => Expect::bool()->default(false),
-            'port' => Expect::int()->min(1)->max(65535)->required(),
-            'ssl_cert_file' => Expect::string()->min(1)->required(),
-            'ssl_key_file' => Expect::string()->min(1)->required(),
+        return new JsonSchema([
+            'type' => 'object',
+            'properties' => [
+                'host' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+                'log_level' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+                'log_requests' => [
+                    'type' => 'boolean',
+                    'default' => false,
+                ],
+                'port' => [
+                    'type' => 'integer',
+                    'minimum' => 1,
+                    'maximum' => 65535,
+                ],
+                'ssl_cert_file' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+                'ssl_key_file' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+            ],
+            'required' => ['host', 'log_level', 'port', 'ssl_cert_file', 'ssl_key_file'],
         ]);
     }
 
diff --git a/src/SingletonProvider/ConfigurationProvider/TranslatorConfigurationProvider.php b/src/SingletonProvider/ConfigurationProvider/TranslatorConfigurationProvider.php
index 7ecacf7d494d3acf8dd207f1dd097e0f91c6ad3f..01519a55d38795ceb974aefcb1fa8f1d87a9b7aa 100644
--- a/src/SingletonProvider/ConfigurationProvider/TranslatorConfigurationProvider.php
+++ b/src/SingletonProvider/ConfigurationProvider/TranslatorConfigurationProvider.php
@@ -5,10 +5,9 @@ declare(strict_types=1);
 namespace Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 
 use Distantmagic\Resonance\Attribute\Singleton;
+use Distantmagic\Resonance\JsonSchema;
 use Distantmagic\Resonance\SingletonProvider\ConfigurationProvider;
 use Distantmagic\Resonance\TranslatorConfiguration;
-use Nette\Schema\Expect;
-use Nette\Schema\Schema;
 
 /**
  * @template-extends ConfigurationProvider<TranslatorConfiguration, object{
@@ -24,11 +23,21 @@ final readonly class TranslatorConfigurationProvider extends ConfigurationProvid
         return 'translator';
     }
 
-    protected function getSchema(): Schema
+    protected function makeSchema(): JsonSchema
     {
-        return Expect::structure([
-            'base_directory' => Expect::string()->min(1)->required(),
-            'default_primary_language' => Expect::string()->min(1)->required(),
+        return new JsonSchema([
+            'type' => 'object',
+            'properties' => [
+                'base_directory' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+                'default_primary_language' => [
+                    'type' => 'string',
+                    'minLength' => 1,
+                ],
+            ],
+            'required' => ['base_directory', 'default_primary_language'],
         ]);
     }