From 1c80bb0efaabe065e9d3fa4fed4ecf5c0388e5b6 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Fri, 14 Aug 2026 02:37:33 +0000 Subject: [PATCH 1/6] feat(metaserver)!: define Classic publisher v2 --- README.md | 4 +- fixtures/metaserver-classic-publisher-v2.json | 270 ++++++++++ metaserver/classic_publisher.go | 310 ++++++++++++ metaserver/classic_publisher_test.go | 470 ++++++++++++++++++ metaserver/game_publisher.go | 2 - metaserver/game_publisher_test.go | 2 +- metaserver/publisher.go | 48 +- metaserver/publisher_test.go | 8 +- ...etaserver-classic-publisher-v2.schema.json | 56 +++ spec/metaserver-publisher.md | 97 +++- tools/package-release.sh | 4 + tools/validate.sh | 3 + 12 files changed, 1231 insertions(+), 43 deletions(-) create mode 100644 fixtures/metaserver-classic-publisher-v2.json create mode 100644 metaserver/classic_publisher.go create mode 100644 metaserver/classic_publisher_test.go create mode 100644 schema/metaserver-classic-publisher-v2.schema.json diff --git a/README.md b/README.md index 29fbd81..0c16bdf 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,9 @@ See [the transport specification](spec/transport.md) and metaserver publishers use the strict RFC 9421/RFC 9530 profile in [the publisher specification](spec/metaserver-publisher.md), including exact canonical classic and Game Protocol 1 body fixtures and the Game publisher -JSON Schema. +JSON Schema. Classic publisher v1 remains frozen with human-password semantics; +the distinct signed Classic v2 profile, schema, Go codec, and conformance +fixtures use `accessCodeRequired` for the open/access-code-protected cutover. The replacement server directory uses the independently versioned bounded model and canonical static JSON contract in [the directory specification](spec/metaserver-directory.md), with generated diff --git a/fixtures/metaserver-classic-publisher-v2.json b/fixtures/metaserver-classic-publisher-v2.json new file mode 100644 index 0000000..18c8841 --- /dev/null +++ b/fixtures/metaserver-classic-publisher-v2.json @@ -0,0 +1,270 @@ +{ + "fixture_version": 1, + "profile": "classic-v2", + "authority": "publish.meta.atrinik.org", + "method": "POST", + "content_type": "application/json", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "maximum_bounds": { + "body_bytes": 4096, + "certificate_der_bytes": 2048, + "name_utf8_bytes": 80, + "players_count": 4294967295, + "version_utf8_bytes": 32, + "text_comment_utf8_bytes": 256, + "hostname_bytes": 253, + "port": 65535 + }, + "positive": [ + { + "name": "public-open-addressless", + "sequence": "101", + "nonce": "202122232425262728292a2b2c2d2e2f", + "created": 1800000000, + "expires": 1800000300, + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":3,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":false}", + "content_digest": "sha-256=:Gr86A+BT7xYperp9yC1Dpg2+AJf20bcontFR6hMJvxY=:", + "path": "/v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish", + "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000000;expires=1800000300;nonce=\"202122232425262728292a2b2c2d2e2f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:Gr86A+BT7xYperp9yC1Dpg2+AJf20bcontFR6hMJvxY=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 101\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000000;expires=1800000300;nonce=\"202122232425262728292a2b2c2d2e2f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "hn4Vp7Yk9oAcsT/ZJa3JjtPNPkojidIirbvpkugwnUncptdh7TMSkOBesO2BNR2UhWvirP798Nz1YLopF7tHOw==", + "signature_header": "atrinik=:hn4Vp7Yk9oAcsT/ZJa3JjtPNPkojidIirbvpkugwnUncptdh7TMSkOBesO2BNR2UhWvirP798Nz1YLopF7tHOw==:" + }, + { + "name": "public-protected-endpoint", + "sequence": "102", + "nonce": "303132333435363738393a3b3c3d3e3f", + "created": 1800000600, + "expires": 1800000900, + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "content_digest": "sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:", + "path": "/v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish", + "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "signature_header": "atrinik=:1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==:" + }, + { + "name": "private-open-endpoint", + "sequence": "103", + "nonce": "404142434445464748494a4b4c4d4e4f", + "created": 1800001200, + "expires": 1800001500, + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":5,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":false,\"accessCodeRequired\":false,\"hostname\":\"private.example.org\",\"port\":13328}", + "content_digest": "sha-256=:vDE4NdX7UjFFd8FvQAw6DJRGcWwkSlDVPQeoJ6WFF4c=:", + "path": "/v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish", + "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001200;expires=1800001500;nonce=\"404142434445464748494a4b4c4d4e4f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:vDE4NdX7UjFFd8FvQAw6DJRGcWwkSlDVPQeoJ6WFF4c=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 103\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001200;expires=1800001500;nonce=\"404142434445464748494a4b4c4d4e4f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "iqRbY+IrGsqLmv33cE2lxOd0cE3dRT70ER5I1W/R+0pqFzpITv4iYu5e0Zpu1GllDo1dRBx3btHG0qFiC/gaEA==", + "signature_header": "atrinik=:iqRbY+IrGsqLmv33cE2lxOd0cE3dRT70ER5I1W/R+0pqFzpITv4iYu5e0Zpu1GllDo1dRBx3btHG0qFiC/gaEA==:" + }, + { + "name": "private-protected-addressless", + "sequence": "104", + "nonce": "505152535455565758595a5b5c5d5e5f", + "created": 1800001800, + "expires": 1800002100, + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":6,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":false,\"accessCodeRequired\":true}", + "content_digest": "sha-256=:s56rYrOnUoGOi8+rE1WX6ztHKCHd7r9kmt8S5DX6uco=:", + "path": "/v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish", + "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001800;expires=1800002100;nonce=\"505152535455565758595a5b5c5d5e5f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:s56rYrOnUoGOi8+rE1WX6ztHKCHd7r9kmt8S5DX6uco=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 104\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001800;expires=1800002100;nonce=\"505152535455565758595a5b5c5d5e5f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "09ZOuTpd+zu1VRmV+Hs12jYGF4bVyxbCgLfNJR0PjsZyDA5bMni8P+4Ni/pYqMs261lxZe43J3iJSiseBxs4ng==", + "signature_header": "atrinik=:09ZOuTpd+zu1VRmV+Hs12jYGF4bVyxbCgLfNJR0PjsZyDA5bMni8P+4Ni/pYqMs261lxZe43J3iJSiseBxs4ng==:" + } + ], + "signature_negative": [ + { + "name": "v2-signature-at-v1", + "positive": "public-protected-endpoint", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v1/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v1\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "error": "invalid_signature" + }, + { + "name": "v2-signature-at-game", + "positive": "public-protected-endpoint", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v1/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-game-publish-v1\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "error": "invalid_signature" + }, + { + "name": "wrong-v2-route", + "positive": "public-protected-endpoint", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/server/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "error": "invalid_signature" + }, + { + "name": "wrong-v2-tag", + "positive": "public-protected-endpoint", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v3\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "error": "invalid_signature" + } + ], + "negative": [ + { + "name": "v1-schema", + "body": "{\"schema\":\"atrinik-classic-publish-v1\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "unsupported_schema" + }, + { + "name": "legacy-password-field", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"passwordRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "noncanonical_json" + }, + { + "name": "wrong-key-order", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"playersCount\":4,\"name\":\"Atrinik Classic\",\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "noncanonical_json" + }, + { + "name": "whitespace", + "body": "{ \"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "noncanonical_json" + }, + { + "name": "duplicate-field", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "noncanonical_json" + }, + { + "name": "unknown-field", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327,\"extra\":true}", + "error": "noncanonical_json" + }, + { + "name": "malformed-boolean", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":\"true\",\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "invalid_json" + }, + { + "name": "missing-policy", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "noncanonical_json" + }, + { + "name": "hostname-without-port", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\"}", + "error": "invalid_endpoint" + }, + { + "name": "port-without-hostname", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"port\":13327}", + "error": "invalid_endpoint" + }, + { + "name": "uppercase-server-id", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13E46C3B910727029646BA5DB7EE5930080F832AF4D9E29F70CF781250A23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "invalid_identity" + }, + { + "name": "noncanonical-certificate", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ=\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "invalid_certificate" + }, + { + "name": "players-overflow", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4294967296,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "invalid_players" + }, + { + "name": "numeric-hostname", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"192.0.2.1\",\"port\":13327}", + "error": "invalid_endpoint" + }, + { + "name": "uppercase-hostname", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"Play.example.org\",\"port\":13327}", + "error": "invalid_endpoint" + }, + { + "name": "control-text", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"bad\\nvalue\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "invalid_text" + }, + { + "name": "trailing-input", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}{}", + "error": "noncanonical_json" + } + ], + "migration": { + "initial_profile": "classic-v1", + "high_water_sequence": "100", + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "cases": [ + { + "name": "higher-v2-upgrade", + "starting_mode": "classic-v1", + "starting_high_water_sequence": "100", + "profile": "classic-v2", + "sequence": "101", + "nonce": "101112131415161718191a1b1c1d1e1f", + "accepted": true, + "upgrades_identity": true, + "minimum_next_sequence": "102" + }, + { + "name": "equal-v2-sequence", + "starting_mode": "classic-v1", + "starting_high_water_sequence": "100", + "profile": "classic-v2", + "sequence": "100", + "nonce": "202122232425262728292a2b2c2d2e2f", + "accepted": false, + "upgrades_identity": false, + "minimum_next_sequence": "101" + }, + { + "name": "stale-v2-sequence", + "starting_mode": "classic-v1", + "starting_high_water_sequence": "100", + "profile": "classic-v2", + "sequence": "99", + "nonce": "303132333435363738393a3b3c3d3e3f", + "accepted": false, + "upgrades_identity": false, + "minimum_next_sequence": "101" + }, + { + "name": "retained-v1-nonce-at-v2", + "starting_mode": "classic-v1", + "starting_high_water_sequence": "100", + "profile": "classic-v2", + "sequence": "101", + "nonce": "000102030405060708090a0b0c0d0e0f", + "accepted": false, + "upgrades_identity": false, + "minimum_next_sequence": "101" + }, + { + "name": "post-upgrade-v1", + "starting_mode": "classic-v2-only", + "starting_high_water_sequence": "101", + "profile": "classic-v1", + "sequence": "102", + "nonce": "404142434445464748494a4b4c4d4e4f", + "accepted": false, + "upgrades_identity": false, + "minimum_next_sequence": "102" + }, + { + "name": "next-v2", + "starting_mode": "classic-v2-only", + "starting_high_water_sequence": "101", + "profile": "classic-v2", + "sequence": "102", + "nonce": "505152535455565758595a5b5c5d5e5f", + "accepted": true, + "upgrades_identity": false, + "minimum_next_sequence": "103" + } + ], + "post_upgrade_mode": "classic-v2-only" + } +} diff --git a/metaserver/classic_publisher.go b/metaserver/classic_publisher.go new file mode 100644 index 0000000..413de54 --- /dev/null +++ b/metaserver/classic_publisher.go @@ -0,0 +1,310 @@ +// Copyright 2026 The Atrinik Project +// SPDX-License-Identifier: MIT + +package metaserver + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/hex" + "encoding/json" + "errors" + "io" + "unicode/utf8" + + metaserverv1 "github.com/atrinik/protocol/gen/go/atrinik/metaserver/v1" +) + +const ( + ClassicV2PublishSchema = "atrinik-classic-publish-v2" + MaximumClassicVersionBytes = 32 + MaximumClassicTextCommentBytes = 256 + MaximumClassicPlayers = uint64(1<<32 - 1) +) + +// ClassicPublishEndpoint is the explicitly configured canonical DNS endpoint +// in a Classic v2 publication. Numeric and discovered addresses are invalid. +type ClassicPublishEndpoint struct { + Hostname string + Port uint32 +} + +// ClassicV2PublishRequest is one complete canonical Classic v2 publisher +// body. ParseClassicV2PublishJSON owns every returned allocation; callers of +// MarshalClassicV2PublishJSON retain ownership of their input. +type ClassicV2PublishRequest struct { + ServerID []byte + CertificateDER []byte + Name string + PlayersCount uint32 + Version string + TextComment string + Public bool + AccessCodeRequired bool + Endpoint *ClassicPublishEndpoint +} + +// ClassicPublishErrorCode is a stable, bounded conformance failure class. It +// never contains rejected input or certificate material. +type ClassicPublishErrorCode string + +const ( + ClassicPublishInvalidJSON ClassicPublishErrorCode = "invalid_json" + ClassicPublishNonCanonicalJSON ClassicPublishErrorCode = "noncanonical_json" + ClassicPublishUnsupportedSchema ClassicPublishErrorCode = "unsupported_schema" + ClassicPublishBodyTooLarge ClassicPublishErrorCode = "body_too_large" + ClassicPublishInvalidIdentity ClassicPublishErrorCode = "invalid_identity" + ClassicPublishInvalidCertificate ClassicPublishErrorCode = "invalid_certificate" + ClassicPublishInvalidText ClassicPublishErrorCode = "invalid_text" + ClassicPublishInvalidPlayers ClassicPublishErrorCode = "invalid_players" + ClassicPublishInvalidEndpoint ClassicPublishErrorCode = "invalid_endpoint" +) + +// ClassicPublishError reports only a stable class suitable for bounded +// diagnostics. +type ClassicPublishError struct { + Code ClassicPublishErrorCode +} + +func (err *ClassicPublishError) Error() string { + return "invalid Classic v2 publication: " + string(err.Code) +} + +// ClassicPublishErrorCodeOf returns a bounded error class and false for +// unrelated errors. +func ClassicPublishErrorCodeOf(err error) (ClassicPublishErrorCode, bool) { + var publishError *ClassicPublishError + if !errors.As(err, &publishError) { + return "", false + } + return publishError.Code, true +} + +type classicV2PublishWire struct { + Schema string `json:"schema"` + ServerID string `json:"serverId"` + Certificate string `json:"certificate"` + Name string `json:"name"` + PlayersCount uint64 `json:"playersCount"` + Version string `json:"version"` + TextComment string `json:"textComment"` + Public bool `json:"public"` + AccessCodeRequired bool `json:"accessCodeRequired"` + Hostname *string `json:"hostname,omitempty"` + Port *uint32 `json:"port,omitempty"` +} + +// ParseClassicV2PublishJSON transactionally validates one complete canonical +// Classic v2 body. Failure returns no partial model. +func ParseClassicV2PublishJSON(input []byte) (*ClassicV2PublishRequest, error) { + if len(input) > MaximumBodyBytes { + return nil, classicPublishFailure(ClassicPublishBodyTooLarge) + } + if len(input) == 0 || !utf8.Valid(input) { + return nil, classicPublishFailure(ClassicPublishInvalidJSON) + } + + decoder := json.NewDecoder(bytes.NewReader(input)) + var wire classicV2PublishWire + if err := decoder.Decode(&wire); err != nil { + return nil, classicPublishFailure(ClassicPublishInvalidJSON) + } + var trailing any + if err := decoder.Decode(&trailing); !errors.Is(err, io.EOF) { + return nil, classicPublishFailure(ClassicPublishNonCanonicalJSON) + } + + request, err := classicV2PublishFromWire(wire) + if err != nil { + return nil, err + } + canonical, err := MarshalClassicV2PublishJSON(request) + if err != nil { + return nil, err + } + if !bytes.Equal(canonical, input) { + return nil, classicPublishFailure(ClassicPublishNonCanonicalJSON) + } + return request, nil +} + +// MarshalClassicV2PublishJSON validates and renders one canonical Classic v2 +// body with no trailing LF or insignificant bytes. +func MarshalClassicV2PublishJSON(request *ClassicV2PublishRequest) ([]byte, error) { + if err := ValidateClassicV2Publish(request); err != nil { + return nil, err + } + wire := classicV2PublishToWire(request) + var encoded bytes.Buffer + encoder := json.NewEncoder(&encoded) + encoder.SetEscapeHTML(false) + if err := encoder.Encode(wire); err != nil { + return nil, classicPublishFailure(ClassicPublishInvalidJSON) + } + body := encoded.Bytes() + if len(body) == 0 || body[len(body)-1] != '\n' { + return nil, classicPublishFailure(ClassicPublishInvalidJSON) + } + body = unescapeClassicLineSeparators(body[:len(body)-1]) + if len(body) == 0 || len(body) > MaximumBodyBytes { + return nil, classicPublishFailure(ClassicPublishBodyTooLarge) + } + return append([]byte(nil), body...), nil +} + +// ValidateClassicV2Publish enforces semantic bounds, certificate identity, +// the P-256 key requirement, and the all-or-none explicit endpoint. +func ValidateClassicV2Publish(request *ClassicV2PublishRequest) error { + if request == nil { + return classicPublishFailure(ClassicPublishInvalidJSON) + } + if len(request.ServerID) != sha256.Size { + return classicPublishFailure(ClassicPublishInvalidIdentity) + } + if !validClassicPublishText(request.Name, 1, MaximumDirectoryNameBytes) || + !validClassicPublishText(request.Version, 1, MaximumClassicVersionBytes) || + !validClassicPublishText(request.TextComment, 0, MaximumClassicTextCommentBytes) { + return classicPublishFailure(ClassicPublishInvalidText) + } + if len(request.CertificateDER) == 0 || + len(request.CertificateDER) > MaximumCertificateDERBytes { + return classicPublishFailure(ClassicPublishInvalidCertificate) + } + fingerprint := sha256.Sum256(request.CertificateDER) + if !bytes.Equal(fingerprint[:], request.ServerID) { + return classicPublishFailure(ClassicPublishInvalidIdentity) + } + certificate, err := x509.ParseCertificate(request.CertificateDER) + if err != nil { + return classicPublishFailure(ClassicPublishInvalidCertificate) + } + publicKey, ok := certificate.PublicKey.(*ecdsa.PublicKey) + if !ok || publicKey.Curve != elliptic.P256() { + return classicPublishFailure(ClassicPublishInvalidCertificate) + } + if request.Endpoint != nil && !validDirectoryEndpoint(&metaserverv1.DirectEndpoint{ + Hostname: request.Endpoint.Hostname, + Port: request.Endpoint.Port, + }) { + return classicPublishFailure(ClassicPublishInvalidEndpoint) + } + return nil +} + +func classicV2PublishFromWire(wire classicV2PublishWire) (*ClassicV2PublishRequest, error) { + if wire.Schema != ClassicV2PublishSchema { + return nil, classicPublishFailure(ClassicPublishUnsupportedSchema) + } + serverID, err := hex.DecodeString(wire.ServerID) + if err != nil || len(serverID) != sha256.Size || wire.ServerID != hex.EncodeToString(serverID) { + return nil, classicPublishFailure(ClassicPublishInvalidIdentity) + } + if wire.Certificate == "" || len(wire.Certificate) > maximumCertificateBase64Bytes { + return nil, classicPublishFailure(ClassicPublishInvalidCertificate) + } + certificate, err := base64.StdEncoding.Strict().DecodeString(wire.Certificate) + if err != nil { + return nil, classicPublishFailure(ClassicPublishInvalidCertificate) + } + if wire.PlayersCount > MaximumClassicPlayers { + return nil, classicPublishFailure(ClassicPublishInvalidPlayers) + } + if (wire.Hostname == nil) != (wire.Port == nil) { + return nil, classicPublishFailure(ClassicPublishInvalidEndpoint) + } + request := &ClassicV2PublishRequest{ + ServerID: serverID, + CertificateDER: certificate, + Name: wire.Name, + PlayersCount: uint32(wire.PlayersCount), + Version: wire.Version, + TextComment: wire.TextComment, + Public: wire.Public, + AccessCodeRequired: wire.AccessCodeRequired, + } + if wire.Hostname != nil { + request.Endpoint = &ClassicPublishEndpoint{ + Hostname: *wire.Hostname, + Port: *wire.Port, + } + } + if err := ValidateClassicV2Publish(request); err != nil { + return nil, err + } + return request, nil +} + +func classicV2PublishToWire(request *ClassicV2PublishRequest) classicV2PublishWire { + wire := classicV2PublishWire{ + Schema: ClassicV2PublishSchema, + ServerID: hex.EncodeToString(request.ServerID), + Certificate: base64.StdEncoding.EncodeToString(request.CertificateDER), + Name: request.Name, + PlayersCount: uint64(request.PlayersCount), + Version: request.Version, + TextComment: request.TextComment, + Public: request.Public, + AccessCodeRequired: request.AccessCodeRequired, + } + if request.Endpoint != nil { + hostname := request.Endpoint.Hostname + port := request.Endpoint.Port + wire.Hostname = &hostname + wire.Port = &port + } + return wire +} + +func classicPublishFailure(code ClassicPublishErrorCode) error { + return &ClassicPublishError{Code: code} +} + +func validClassicPublishText(value string, minimum int, maximum int) bool { + if !utf8.ValidString(value) || len(value) < minimum || len(value) > maximum { + return false + } + for _, current := range value { + if current <= '\u001f' || current == '\u007f' { + return false + } + } + return true +} + +func unescapeClassicLineSeparators(input []byte) []byte { + output := make([]byte, 0, len(input)) + for index := 0; index < len(input); { + if input[index] != '\\' { + output = append(output, input[index]) + index++ + continue + } + runEnd := index + for runEnd < len(input) && input[runEnd] == '\\' { + runEnd++ + } + runLength := runEnd - index + if runLength%2 == 1 && runEnd+5 <= len(input) { + replacement := []byte(nil) + switch string(input[runEnd : runEnd+5]) { + case "u2028": + replacement = []byte("\u2028") + case "u2029": + replacement = []byte("\u2029") + } + if replacement != nil { + output = append(output, input[index:runEnd-1]...) + output = append(output, replacement...) + index = runEnd + 5 + continue + } + } + output = append(output, input[index:runEnd]...) + index = runEnd + } + return output +} diff --git a/metaserver/classic_publisher_test.go b/metaserver/classic_publisher_test.go new file mode 100644 index 0000000..5064933 --- /dev/null +++ b/metaserver/classic_publisher_test.go @@ -0,0 +1,470 @@ +// Copyright 2026 The Atrinik Project +// SPDX-License-Identifier: MIT + +package metaserver_test + +import ( + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "encoding/json" + "errors" + "os" + "strconv" + "strings" + "testing" + + "github.com/atrinik/protocol/metaserver" +) + +type classicV2PositiveFixture struct { + Name string `json:"name"` + Sequence string `json:"sequence"` + Nonce string `json:"nonce"` + Created int64 `json:"created"` + Expires int64 `json:"expires"` + Body string `json:"body"` + ContentDigest string `json:"content_digest"` + Path string `json:"path"` + SignatureInput string `json:"signature_input"` + SignatureBase string `json:"signature_base"` + SignatureBase64 string `json:"signature_base64"` + SignatureHeader string `json:"signature_header"` +} + +type classicV2NegativeFixture struct { + Name string `json:"name"` + Body string `json:"body"` + Error metaserver.ClassicPublishErrorCode `json:"error"` +} + +type classicV2SignatureNegativeFixture struct { + Name string `json:"name"` + Positive string `json:"positive"` + SignatureBase string `json:"signature_base"` + SignatureBase64 string `json:"signature_base64"` + Error string `json:"error"` +} + +type classicV2MigrationCase struct { + Name string `json:"name"` + StartingMode string `json:"starting_mode"` + StartingHighWater string `json:"starting_high_water_sequence"` + Profile string `json:"profile"` + Sequence string `json:"sequence"` + Nonce string `json:"nonce"` + Accepted bool `json:"accepted"` + UpgradesIdentity bool `json:"upgrades_identity"` + MinimumNextSequence string `json:"minimum_next_sequence"` +} + +type classicV2Fixture struct { + FixtureVersion int `json:"fixture_version"` + Profile string `json:"profile"` + Authority string `json:"authority"` + Method string `json:"method"` + ContentType string `json:"content_type"` + ServerID string `json:"server_id"` + CertificateDERBase64 string `json:"certificate_der_base64"` + MaximumBounds struct { + BodyBytes int `json:"body_bytes"` + CertificateDERBytes int `json:"certificate_der_bytes"` + NameUTF8Bytes int `json:"name_utf8_bytes"` + PlayersCount uint64 `json:"players_count"` + VersionUTF8Bytes int `json:"version_utf8_bytes"` + TextCommentUTF8Bytes int `json:"text_comment_utf8_bytes"` + HostnameBytes int `json:"hostname_bytes"` + Port uint32 `json:"port"` + } `json:"maximum_bounds"` + Positive []classicV2PositiveFixture `json:"positive"` + SignatureNegative []classicV2SignatureNegativeFixture `json:"signature_negative"` + Negative []classicV2NegativeFixture `json:"negative"` + Migration struct { + InitialProfile string `json:"initial_profile"` + HighWater string `json:"high_water_sequence"` + RetainedNonces []string `json:"retained_nonces"` + Cases []classicV2MigrationCase `json:"cases"` + PostUpgradeMode string `json:"post_upgrade_mode"` + } `json:"migration"` +} + +func TestClassicV2PublisherSignatureNegativeFixtures(t *testing.T) { + fixture := loadClassicV2Fixture(t) + if len(fixture.SignatureNegative) != 4 { + t.Fatal("signature-negative fixture does not cover route and tag domains") + } + certificate := decodeBase64(t, fixture.CertificateDERBase64) + positiveSignatures := make(map[string]string, len(fixture.Positive)) + for _, positive := range fixture.Positive { + positiveSignatures[positive.Name] = positive.SignatureBase64 + } + for _, test := range fixture.SignatureNegative { + t.Run(test.Name, func(t *testing.T) { + if test.Error != "invalid_signature" || test.Positive == "" || test.SignatureBase == "" || + positiveSignatures[test.Positive] != test.SignatureBase64 { + t.Fatal("signature-negative fixture metadata is incomplete") + } + signature := decodeBase64(t, test.SignatureBase64) + if err := metaserver.VerifyCertificateSignature( + certificate, + fixture.ServerID, + test.SignatureBase, + signature, + ); !errors.Is(err, metaserver.ErrInvalidSignature) { + t.Fatalf("negative signature verified: %v", err) + } + }) + } +} + +func TestClassicV2PublisherGoldenFixtures(t *testing.T) { + fixture := loadClassicV2Fixture(t) + if fixture.FixtureVersion != 1 || fixture.Profile != "classic-v2" || + fixture.Method != "POST" || fixture.ContentType != metaserver.ContentType || + len(fixture.Positive) != 4 { + t.Fatal("fixture metadata does not describe the complete Classic v2 matrix") + } + if fixture.MaximumBounds.BodyBytes != metaserver.MaximumBodyBytes || + fixture.MaximumBounds.CertificateDERBytes != metaserver.MaximumCertificateDERBytes || + fixture.MaximumBounds.NameUTF8Bytes != metaserver.MaximumDirectoryNameBytes || + fixture.MaximumBounds.PlayersCount != metaserver.MaximumClassicPlayers || + fixture.MaximumBounds.VersionUTF8Bytes != metaserver.MaximumClassicVersionBytes || + fixture.MaximumBounds.TextCommentUTF8Bytes != metaserver.MaximumClassicTextCommentBytes || + fixture.MaximumBounds.HostnameBytes != 253 || fixture.MaximumBounds.Port != 65_535 { + t.Fatal("fixture bounds differ from the Classic v2 contract") + } + certificate := decodeBase64(t, fixture.CertificateDERBase64) + seen := make(map[string]bool) + for _, test := range fixture.Positive { + t.Run(test.Name, func(t *testing.T) { + request, err := metaserver.ParseClassicV2PublishJSON([]byte(test.Body)) + if err != nil { + t.Fatal(err) + } + encoded, err := metaserver.MarshalClassicV2PublishJSON(request) + if err != nil || string(encoded) != test.Body { + t.Fatalf("fixture did not round-trip byte-identically: %v", err) + } + if hex.EncodeToString(request.ServerID) != fixture.ServerID || + base64.StdEncoding.EncodeToString(request.CertificateDER) != fixture.CertificateDERBase64 { + t.Fatal("fixture identity and certificate do not match their metadata") + } + parameters := classicV2FixtureParameters(t, fixture, test, metaserver.ClassicV2Profile) + components, err := metaserver.Build(parameters, encoded) + if err != nil { + t.Fatal(err) + } + if test.Expires != test.Created+metaserver.SignatureValidity || + components.Path != test.Path || components.ContentDigest != test.ContentDigest || + components.SignatureInput != test.SignatureInput || + components.SignatureBase != test.SignatureBase { + t.Fatal("constructed Classic v2 signature components differ from the fixture") + } + signature := decodeBase64(t, test.SignatureBase64) + if test.SignatureHeader != "atrinik=:"+test.SignatureBase64+":" { + t.Fatal("fixture signature header is not canonical") + } + if err := metaserver.VerifyCertificateSignature( + certificate, + fixture.ServerID, + components.SignatureBase, + signature, + ); err != nil { + t.Fatalf("fixture signature did not verify: %v", err) + } + + for _, profile := range []metaserver.Profile{ + metaserver.ClassicV1Profile, + metaserver.GameProfile, + } { + parameters.Profile = profile + crossed, err := metaserver.Build(parameters, encoded) + if err != nil { + t.Fatal(err) + } + if err := metaserver.VerifyCertificateSignature( + certificate, + fixture.ServerID, + crossed.SignatureBase, + signature, + ); !errors.Is(err, metaserver.ErrInvalidSignature) { + t.Fatalf("Classic v2 signature crossed profile %d: %v", profile, err) + } + } + + key := strconv.FormatBool(request.Public) + "/" + + strconv.FormatBool(request.AccessCodeRequired) + "/" + + strconv.FormatBool(request.Endpoint != nil) + seen[key] = true + }) + } + for _, required := range []string{ + "true/false/false", + "true/true/true", + "false/false/true", + "false/true/false", + } { + if !seen[required] { + t.Fatalf("positive fixture matrix is missing %s", required) + } + } +} + +func TestClassicV2PublisherNegativeFixtures(t *testing.T) { + fixture := loadClassicV2Fixture(t) + if len(fixture.Negative) < 12 { + t.Fatal("negative fixture does not cover the canonical contract") + } + for _, test := range fixture.Negative { + t.Run(test.Name, func(t *testing.T) { + request, err := metaserver.ParseClassicV2PublishJSON([]byte(test.Body)) + if request != nil { + t.Fatal("rejected body returned a partial request") + } + code, ok := metaserver.ClassicPublishErrorCodeOf(err) + if !ok || code != test.Error { + t.Fatalf("rejected body returned (%q, %v), want %q", code, err, test.Error) + } + }) + } +} + +func TestClassicV2MigrationFixture(t *testing.T) { + fixture := loadClassicV2Fixture(t) + if fixture.Migration.InitialProfile != "classic-v1" || + fixture.Migration.HighWater != "100" || + len(fixture.Migration.RetainedNonces) == 0 || + fixture.Migration.PostUpgradeMode != "classic-v2-only" { + t.Fatal("migration fixture lacks the frozen v1 starting state or irreversible v2 result") + } + want := map[string]struct { + starting string + highWater string + accepted bool + upgrades bool + minimum string + }{ + "higher-v2-upgrade": {"classic-v1", "100", true, true, "102"}, + "equal-v2-sequence": {"classic-v1", "100", false, false, "101"}, + "stale-v2-sequence": {"classic-v1", "100", false, false, "101"}, + "retained-v1-nonce-at-v2": {"classic-v1", "100", false, false, "101"}, + "post-upgrade-v1": {"classic-v2-only", "101", false, false, "102"}, + "next-v2": {"classic-v2-only", "101", true, false, "103"}, + } + if len(fixture.Migration.Cases) != len(want) { + t.Fatalf("migration case count %d, want %d", len(fixture.Migration.Cases), len(want)) + } + for _, test := range fixture.Migration.Cases { + expected, ok := want[test.Name] + if !ok || test.StartingMode != expected.starting || + test.StartingHighWater != expected.highWater || + test.Accepted != expected.accepted || + test.UpgradesIdentity != expected.upgrades || + test.MinimumNextSequence != expected.minimum { + t.Fatalf("migration case %q differs from the declared lineage", test.Name) + } + if _, err := strconv.ParseUint(test.StartingHighWater, 10, 64); err != nil { + t.Fatalf("migration case %q has invalid starting high water: %v", test.Name, err) + } + if _, err := strconv.ParseUint(test.Sequence, 10, 64); err != nil { + t.Fatalf("migration case %q has invalid sequence: %v", test.Name, err) + } + if nonce, err := hex.DecodeString(test.Nonce); err != nil || len(nonce) != 16 { + t.Fatalf("migration case %q has invalid nonce", test.Name) + } + } +} + +func TestClassicV2PublisherValidationClasses(t *testing.T) { + fixture := loadClassicV2Fixture(t) + base, err := metaserver.ParseClassicV2PublishJSON([]byte(fixture.Positive[1].Body)) + if err != nil { + t.Fatal(err) + } + tests := []struct { + name string + mutate func(*metaserver.ClassicV2PublishRequest) + code metaserver.ClassicPublishErrorCode + }{ + {"identity", func(value *metaserver.ClassicV2PublishRequest) { value.ServerID[0] ^= 1 }, metaserver.ClassicPublishInvalidIdentity}, + {"name", func(value *metaserver.ClassicV2PublishRequest) { value.Name = "" }, metaserver.ClassicPublishInvalidText}, + {"name too long", func(value *metaserver.ClassicV2PublishRequest) { value.Name = strings.Repeat("n", 81) }, metaserver.ClassicPublishInvalidText}, + {"version", func(value *metaserver.ClassicV2PublishRequest) { value.Version = strings.Repeat("v", 33) }, metaserver.ClassicPublishInvalidText}, + {"comment", func(value *metaserver.ClassicV2PublishRequest) { value.TextComment = "bad\nvalue" }, metaserver.ClassicPublishInvalidText}, + {"comment too long", func(value *metaserver.ClassicV2PublishRequest) { value.TextComment = strings.Repeat("c", 257) }, metaserver.ClassicPublishInvalidText}, + {"endpoint", func(value *metaserver.ClassicV2PublishRequest) { value.Endpoint.Hostname = "192.0.2.1" }, metaserver.ClassicPublishInvalidEndpoint}, + {"certificate", func(value *metaserver.ClassicV2PublishRequest) { + value.CertificateDER = []byte("not a certificate") + digest := sha256.Sum256(value.CertificateDER) + value.ServerID = append([]byte(nil), digest[:]...) + }, metaserver.ClassicPublishInvalidCertificate}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + value := cloneClassicV2Request(base) + test.mutate(value) + err := metaserver.ValidateClassicV2Publish(value) + code, ok := metaserver.ClassicPublishErrorCodeOf(err) + if !ok || code != test.code { + t.Fatalf("invalid model returned (%q, %v), want %q", code, err, test.code) + } + }) + } + if code := classicPublishErrorCode(t, metaserver.ValidateClassicV2Publish(nil)); code != metaserver.ClassicPublishInvalidJSON { + t.Fatalf("nil request returned %q", code) + } +} + +func TestClassicV2PublisherPreservesClassicTextScalars(t *testing.T) { + fixture := loadClassicV2Fixture(t) + request, err := metaserver.ParseClassicV2PublishJSON([]byte(fixture.Positive[0].Body)) + if err != nil { + t.Fatal(err) + } + request.TextComment = "line\u2028paragraph\u2029C1:\u0085 noncharacters:\ufffe\uffff slash:\\u2028 combo:\\\u2028" + encoded, err := metaserver.MarshalClassicV2PublishJSON(request) + if err != nil { + t.Fatal(err) + } + if strings.Count(string(encoded), `\u2028`) != 1 || strings.Contains(string(encoded), `\u2029`) { + t.Fatal("Classic scalar values were emitted as noncanonical JSON escapes") + } + if !strings.Contains(string(encoded), `slash:\\u2028`) { + t.Fatal("literal reverse-solidus text was changed while rendering scalars") + } + parsed, err := metaserver.ParseClassicV2PublishJSON(encoded) + if err != nil || parsed.TextComment != request.TextComment { + t.Fatalf("Classic scalar text did not round-trip: %v", err) + } +} + +func TestClassicV2PublisherMaximumModel(t *testing.T) { + fixture := loadClassicV2Fixture(t) + request, err := metaserver.ParseClassicV2PublishJSON([]byte(fixture.Positive[1].Body)) + if err != nil { + t.Fatal(err) + } + request.Name = strings.Repeat("n", metaserver.MaximumDirectoryNameBytes) + request.PlayersCount = ^uint32(0) + request.Version = strings.Repeat("v", metaserver.MaximumClassicVersionBytes) + request.TextComment = strings.Repeat("c", metaserver.MaximumClassicTextCommentBytes) + request.Endpoint.Hostname = strings.Repeat("a", 63) + "." + + strings.Repeat("b", 63) + "." + strings.Repeat("c", 63) + "." + strings.Repeat("d", 61) + request.Endpoint.Port = 65_535 + encoded, err := metaserver.MarshalClassicV2PublishJSON(request) + if err != nil { + t.Fatal(err) + } + if len(request.Endpoint.Hostname) != fixture.MaximumBounds.HostnameBytes { + t.Fatal("maximum hostname fixture does not reach its byte bound") + } + if _, err := metaserver.ParseClassicV2PublishJSON(encoded); err != nil { + t.Fatalf("maximum valid model did not round-trip: %v", err) + } +} + +func TestClassicV2PublisherInputBounds(t *testing.T) { + fixture := loadClassicV2Fixture(t) + tests := []struct { + name string + body []byte + code metaserver.ClassicPublishErrorCode + }{ + {"empty", nil, metaserver.ClassicPublishInvalidJSON}, + {"malformed", []byte(`{"schema":`), metaserver.ClassicPublishInvalidJSON}, + {"invalid UTF-8", []byte{'{', 0xff, '}'}, metaserver.ClassicPublishInvalidJSON}, + {"maximum plus one", []byte(strings.Repeat("x", metaserver.MaximumBodyBytes+1)), metaserver.ClassicPublishBodyTooLarge}, + {"trailing LF", []byte(fixture.Positive[0].Body + "\n"), metaserver.ClassicPublishNonCanonicalJSON}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + _, err := metaserver.ParseClassicV2PublishJSON(test.body) + if code := classicPublishErrorCode(t, err); code != test.code { + t.Fatalf("invalid input returned %q, want %q", code, test.code) + } + }) + } +} + +func FuzzClassicV2PublishJSON(f *testing.F) { + fixture := loadClassicV2Fixture(f) + for _, test := range fixture.Positive { + f.Add([]byte(test.Body)) + } + for _, test := range fixture.Negative { + f.Add([]byte(test.Body)) + } + f.Fuzz(func(t *testing.T, input []byte) { + request, err := metaserver.ParseClassicV2PublishJSON(input) + if err != nil { + if _, ok := metaserver.ClassicPublishErrorCodeOf(err); !ok { + t.Fatalf("unbounded error type: %T", err) + } + return + } + encoded, err := metaserver.MarshalClassicV2PublishJSON(request) + if err != nil || string(encoded) != string(input) { + t.Fatal("accepted body did not round-trip canonically") + } + }) +} + +func loadClassicV2Fixture(t testing.TB) classicV2Fixture { + t.Helper() + data, err := os.ReadFile("../fixtures/metaserver-classic-publisher-v2.json") + if err != nil { + t.Fatal(err) + } + var fixture classicV2Fixture + if err := json.Unmarshal(data, &fixture); err != nil { + t.Fatal(err) + } + return fixture +} + +func classicV2FixtureParameters( + t *testing.T, + fixture classicV2Fixture, + test classicV2PositiveFixture, + profile metaserver.Profile, +) metaserver.Parameters { + t.Helper() + sequence, err := strconv.ParseUint(test.Sequence, 10, 64) + if err != nil { + t.Fatal(err) + } + nonceBytes, err := hex.DecodeString(test.Nonce) + if err != nil || len(nonceBytes) != 16 { + t.Fatalf("invalid fixture nonce: %v", err) + } + var nonce [16]byte + copy(nonce[:], nonceBytes) + return metaserver.Parameters{ + Profile: profile, + Authority: fixture.Authority, + ServerID: fixture.ServerID, + Sequence: sequence, + Nonce: nonce, + Created: test.Created, + } +} + +func cloneClassicV2Request(value *metaserver.ClassicV2PublishRequest) *metaserver.ClassicV2PublishRequest { + clone := *value + clone.ServerID = append([]byte(nil), value.ServerID...) + clone.CertificateDER = append([]byte(nil), value.CertificateDER...) + if value.Endpoint != nil { + endpoint := *value.Endpoint + clone.Endpoint = &endpoint + } + return &clone +} + +func classicPublishErrorCode(t *testing.T, err error) metaserver.ClassicPublishErrorCode { + t.Helper() + code, ok := metaserver.ClassicPublishErrorCodeOf(err) + if !ok { + t.Fatalf("unexpected error %v", err) + } + return code +} diff --git a/metaserver/game_publisher.go b/metaserver/game_publisher.go index da0fea3..5b519f0 100644 --- a/metaserver/game_publisher.go +++ b/metaserver/game_publisher.go @@ -21,8 +21,6 @@ import ( const GamePublishSchema = "atrinik-game-publish-v1" -const maximumCertificateBase64Bytes = ((MaximumCertificateDERBytes + 2) / 3) * 4 - // GamePublishRequest is one authenticated GP1 publication body after strict // canonical JSON validation. CertificateDER and Server remain caller-owned // when passed to MarshalGamePublishJSON; ParseGamePublishJSON returns new diff --git a/metaserver/game_publisher_test.go b/metaserver/game_publisher_test.go index 51d27a5..49d5381 100644 --- a/metaserver/game_publisher_test.go +++ b/metaserver/game_publisher_test.go @@ -118,7 +118,7 @@ func TestGamePublisherGoldenFixture(t *testing.T) { t.Fatalf("fixture signature did not verify: %v", err) } - parameters.Profile = metaserver.ClassicProfile + parameters.Profile = metaserver.ClassicV1Profile classic, err := metaserver.Build(parameters, encoded) if err != nil { t.Fatal(err) diff --git a/metaserver/publisher.go b/metaserver/publisher.go index 5bdabd9..d572ddd 100644 --- a/metaserver/publisher.go +++ b/metaserver/publisher.go @@ -23,23 +23,26 @@ import ( ) const ( - SignatureLabel = "atrinik" - SignatureAlgorithm = "ecdsa-p256-sha256" - ContentType = "application/json" - MaximumBodyBytes = 4096 - MaximumCertificateDERBytes = 2048 - MaximumClockSkew = 300 - ClassicSignatureTag = "atrinik-classic-publish-v1" - GameSignatureTag = "atrinik-game-publish-v1" - SignatureValidity = MaximumClockSkew - classicPathPrefix = "/v1/classic/servers/" - gamePathPrefix = "/v1/servers/" - publishPathSuffix = "/publish" - coveredComponents = "(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\")" - maximumSFInteger = int64(999_999_999_999_999) - serverIDHexLength = 64 - p256SignatureLength = 64 - p256CoordinateLength = 32 + SignatureLabel = "atrinik" + SignatureAlgorithm = "ecdsa-p256-sha256" + ContentType = "application/json" + MaximumBodyBytes = 4096 + MaximumCertificateDERBytes = 2048 + MaximumClockSkew = 300 + ClassicV1SignatureTag = "atrinik-classic-publish-v1" + ClassicV2SignatureTag = "atrinik-classic-publish-v2" + GameSignatureTag = "atrinik-game-publish-v1" + SignatureValidity = MaximumClockSkew + classicV1PathPrefix = "/v1/classic/servers/" + classicV2PathPrefix = "/v2/classic/servers/" + gamePathPrefix = "/v1/servers/" + publishPathSuffix = "/publish" + coveredComponents = "(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\")" + maximumSFInteger = int64(999_999_999_999_999) + serverIDHexLength = 64 + p256SignatureLength = 64 + p256CoordinateLength = 32 + maximumCertificateBase64Bytes = ((MaximumCertificateDERBytes + 2) / 3) * 4 ) var ( @@ -52,8 +55,9 @@ var ( type Profile uint8 const ( - ClassicProfile Profile = iota + 1 - GameProfile + ClassicV1Profile Profile = 1 + GameProfile Profile = 2 + ClassicV2Profile Profile = 3 ) // Parameters are the bounded per-request values carried by Signature-Input @@ -189,8 +193,10 @@ func encodeP256Signature(r, s *big.Int) ([]byte, error) { func profileValues(profile Profile, serverID string) (string, string, bool) { switch profile { - case ClassicProfile: - return classicPathPrefix + serverID + publishPathSuffix, ClassicSignatureTag, true + case ClassicV1Profile: + return classicV1PathPrefix + serverID + publishPathSuffix, ClassicV1SignatureTag, true + case ClassicV2Profile: + return classicV2PathPrefix + serverID + publishPathSuffix, ClassicV2SignatureTag, true case GameProfile: return gamePathPrefix + serverID + publishPathSuffix, GameSignatureTag, true default: diff --git a/metaserver/publisher_test.go b/metaserver/publisher_test.go index ba383ec..9a4d217 100644 --- a/metaserver/publisher_test.go +++ b/metaserver/publisher_test.go @@ -47,7 +47,7 @@ type publisherFixture struct { func TestPublisherGoldenFixture(t *testing.T) { fixture := loadPublisherFixture(t) - parameters := fixtureParameters(t, fixture, metaserver.ClassicProfile) + parameters := fixtureParameters(t, fixture, metaserver.ClassicV1Profile) components, err := metaserver.Build(parameters, []byte(fixture.Body)) if err != nil { t.Fatal(err) @@ -109,7 +109,7 @@ func TestPublisherFixtureRejectsMutations(t *testing.T) { {"authority", strings.Replace(fixture.SignatureBase, fixture.Authority, "canary.publish.meta.atrinik.org", 1)}, {"path", strings.Replace(fixture.SignatureBase, "/v1/classic/", "/v1/", 1)}, {"sequence", strings.Replace(fixture.SignatureBase, fixture.Sequence, "7", 1)}, - {"protocol tag", strings.Replace(fixture.SignatureBase, metaserver.ClassicSignatureTag, metaserver.GameSignatureTag, 1)}, + {"protocol tag", strings.Replace(fixture.SignatureBase, metaserver.ClassicV1SignatureTag, metaserver.GameSignatureTag, 1)}, } for _, mutation := range mutations { t.Run(mutation.name, func(t *testing.T) { @@ -168,7 +168,7 @@ func TestPublisherFixtureRejectsMutations(t *testing.T) { func TestPublisherComponentBounds(t *testing.T) { fixture := loadPublisherFixture(t) - valid := fixtureParameters(t, fixture, metaserver.ClassicProfile) + valid := fixtureParameters(t, fixture, metaserver.ClassicV1Profile) tests := []struct { name string parameters metaserver.Parameters @@ -251,7 +251,7 @@ func FuzzPublisher(f *testing.F) { var nonce [16]byte copy(nonce[:], nonceBytes) parameters := metaserver.Parameters{ - Profile: metaserver.ClassicProfile, + Profile: metaserver.ClassicV1Profile, Authority: authority, ServerID: serverID, Sequence: sequence, diff --git a/schema/metaserver-classic-publisher-v2.schema.json b/schema/metaserver-classic-publisher-v2.schema.json new file mode 100644 index 0000000..102f14e --- /dev/null +++ b/schema/metaserver-classic-publisher-v2.schema.json @@ -0,0 +1,56 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://protocol.atrinik.org/schema/metaserver-classic-publisher-v2.schema.json", + "title": "Atrinik Classic metaserver publication v2", + "type": "object", + "additionalProperties": false, + "required": [ + "schema", + "serverId", + "certificate", + "name", + "playersCount", + "version", + "textComment", + "public", + "accessCodeRequired" + ], + "properties": { + "schema": {"const": "atrinik-classic-publish-v2"}, + "serverId": {"$ref": "#/$defs/digest"}, + "certificate": { + "type": "string", + "minLength": 4, + "maxLength": 2732, + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "$comment": "Canonical padded base64 of 1 through 2048 DER bytes; the certificate must carry an ECDSA P-256 key and its SHA-256 digest must equal serverId." + }, + "name": {"type": "string", "minLength": 1, "maxLength": 80}, + "playersCount": {"type": "integer", "minimum": 0, "maximum": 4294967295}, + "version": {"type": "string", "minLength": 1, "maxLength": 32}, + "textComment": {"type": "string", "maxLength": 256}, + "public": {"type": "boolean"}, + "accessCodeRequired": {"type": "boolean"}, + "hostname": { + "type": "string", + "minLength": 1, + "maxLength": 253, + "pattern": "^(?=.*[a-z])(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$", + "$comment": "When hostname is present, port is mandatory. Canonical non-transitional UTS #46 A-label validation remains a semantic requirement." + }, + "port": {"type": "integer", "minimum": 1, "maximum": 65535} + }, + "allOf": [ + { + "if": {"required": ["hostname"]}, + "then": {"required": ["port"]}, + "else": {"not": {"required": ["port"]}} + } + ], + "$defs": { + "digest": { + "type": "string", + "pattern": "^[0-9a-f]{64}$" + } + } +} diff --git a/spec/metaserver-publisher.md b/spec/metaserver-publisher.md index d941835..85aabbf 100644 --- a/spec/metaserver-publisher.md +++ b/spec/metaserver-publisher.md @@ -1,4 +1,4 @@ -# Metaserver publisher authentication v1 +# Metaserver publisher authentication This specification defines Atrinik's certificate-bound, one-request metaserver publisher authentication profile. It is independent from gameplay @@ -16,11 +16,12 @@ key MUST be ECDSA P-256. Certificate chain trust and wall-clock validity are not identity inputs: clients pin the exact DER digest, and proof of the matching private key authorizes publication. -Two profiles are deliberately separate: +Three profiles are deliberately separate: | Profile | Exact path | Signature `tag` | | --- | --- | --- | -| Classic | `/v1/classic/servers/{server-id}/publish` | `atrinik-classic-publish-v1` | +| Classic v1 (frozen) | `/v1/classic/servers/{server-id}/publish` | `atrinik-classic-publish-v1` | +| Classic v2 | `/v2/classic/servers/{server-id}/publish` | `atrinik-classic-publish-v2` | | Game Protocol 1 | `/v1/servers/{server-id}/publish` | `atrinik-game-publish-v1` | Both use signature label `atrinik`, algorithm parameter @@ -69,6 +70,50 @@ canonical JSON integer from 1 through 65535. A publisher that has not explicitly opted into a DNS endpoint omits both fields; request-source and discovered numeric addresses are never substituted. +Classic v1 is frozen with this exact `passwordRequired` human-password +meaning. A receiver MUST NOT reinterpret, normalize, or project a v1 body as +an access-code publication. Its route, schema, signature tag, key order, and +field semantics remain an independent pre-cutover contract until the global +v1 retirement gate described below. + +### Classic v2 body + +The Classic v2 body has the same strict UTF-8, canonical JSON, identity, +certificate, display, player-count, version, comment, and optional endpoint +rules as Classic v1. Its keys occur exactly in this order: + +```text +schema, serverId, certificate, name, playersCount, version, textComment, +public, accessCodeRequired[, hostname, port] +``` + +`schema` is `atrinik-classic-publish-v2`. `accessCodeRequired` is a mandatory +JSON Boolean describing one exhaustive configured server policy: + +- `false` is open access. No player access capability is required before + candidate disclosure or after QUIC; a reachable endpoint and any candidate + disclosed by unauthenticated rendezvous are public routing information. +- `true` requires the current process launch's one full access code. Its + rendezvous capability gates candidate disclosure, and its independent join + capability gates every post-QUIC setup, including direct connections. + +The Boolean is configured policy, not current credential health. Generation, +artifact, signaling, expiry, or verification failure MUST NOT cause a +protected publisher to emit `false`; the server remains protected, withdraws, +or fails closed. No access code, access ID, secret, token, verifier, expiry, +proof, candidate, password, or authentication result is publisher data. +There is no password-only, invite-only, join-only, partially protected, or +hybrid v2 body. + +The normative schema is +`schema/metaserver-classic-publisher-v2.schema.json`. The byte-identical open, +protected, public, private, endpoint-present, and endpoint-absent bodies, +digests, signature inputs, bases, signatures, negative cases, and replay +migration vectors are in +`fixtures/metaserver-classic-publisher-v2.json`. Exact key ordering, UTF-8 byte +limits, certificate identity, canonical base64, canonical JSON, and IDNA +semantics remain requirements where JSON Schema cannot express them. + ### Game Protocol 1 body The Game Protocol 1 body is UTF-8 JSON subject to the same no-BOM, @@ -164,7 +209,8 @@ policy. Only after certificate and signature verification may a receiver charge an identity quota or inspect the body as authoritative input. For each server -identity, one serialized atomic publication operation: +identity and its selected profile, one serialized atomic publication +operation: 1. rejects a nonce already present in the bounded replay window; 2. requires `sequence` to be greater than the stored sequence; @@ -188,8 +234,24 @@ ordering remains authoritative after nonce pruning. An authenticated heartbeat with equivalent public content refreshes presence but does not advance the visible directory revision. A public-content change or expiry does. +Classic v1 and v2 share one monotonic sequence and retained-nonce lineage per +server identity. The first accepted v2 operation MUST be strictly above the v1 +high-water mark, reject any nonce retained from either Classic profile, and +atomically retire the identity's v1 presence, listing, rendezvous generation, +controls, pending authorizations, and tickets before v2 becomes usable. That +commit irreversibly marks the identity Classic-v2-only: every later v1 +publication fails closed even with a higher sequence or fresh nonce. The v2 +success response and later replay conflicts continue the same exact +`minimumNextSequence` lineage. A failed v2 attempt changes neither the v1 +state nor the upgrade marker. + +Classic v1 and v2 bodies, routes, schemas, signature tags, and signatures are +mutually non-replayable. A v1 password listing is never emitted in a v2 +directory or admitted to a v2 rendezvous room. Game Protocol 1 retains its +independent sequence, nonce, body, route, and public behavior. + Successful responses and all 409/429 responses use `Cache-Control: no-store`. -The success body for both profiles is exactly +The success body for all profiles is exactly `{"status":"ok","rendezvousToken":"{64-lower-hex}"}`. A replay conflict is exactly `{"error":{"code":"publish_replay","minimumNextSequence":"{uint64}"}}`. @@ -205,14 +267,21 @@ and directory artifacts. ## Compatibility and ownership -This is a new, fail-closed profile. Classic OTP/COTP requests are a temporary -compatibility route and are not accepted on either v1 path. Their removal -requires a deployed publisher hostname, supported-consumer cutover, and a -documented rollback window. Game Protocol 1 never uses the classic form, key, -route, schema, or signature tag. +Classic v2 is a new, fail-closed profile. A cutover Classic server publishes +v2 only and never negotiates or falls back to v1. Temporary v1 availability +serves only identities which have not upgraded. At the explicit coordinated +global retirement gate, receivers reject every new Classic v1 publication, +expire or tombstone remaining v1 presence/listings/rooms, converge the v2-only +directory, and then contract retired state. This gate is not an indefinite +dual-producer policy, and recovery after production v2 exposure is +roll-forward. + +Game Protocol 1 never uses either Classic form, key, route, schema, signature +tag, migration marker, or replay lineage. Its `passwordRequired` semantics and +generated directory/protobuf models are unchanged. The protocol repository owns these canonical inputs and the shared fixture. -The metaserver Worker owns strict HTTP parsing, authentication, replay storage, -atomic listing mutation, rate limits, and redaction. Classic and Go servers own -private-key access, crash-safe sequence persistence, request construction, -backoff, and response handling. +The metaserver Worker owns strict HTTP parsing, authentication, the atomic +Classic v1-to-v2 migration, replay storage, listing mutation, rate limits, and +redaction. Classic and Go servers own private-key access, crash-safe sequence +persistence, request construction, backoff, and response handling. diff --git a/tools/package-release.sh b/tools/package-release.sh index 508b302..0268cb4 100755 --- a/tools/package-release.sh +++ b/tools/package-release.sh @@ -68,8 +68,10 @@ git archive --format=tar --prefix="atrinik-protocol-${version}/" HEAD \ cp gen/descriptor/atrinik-game-v1.binpb "${output}/" cp fixtures/framing.json fixtures/metaserver-directory-v1.json \ fixtures/metaserver-game-publisher-v1.json \ + fixtures/metaserver-classic-publisher-v2.json \ fixtures/metaserver-publisher-v1.json \ schema/metaserver-directory-v1.schema.json \ + schema/metaserver-classic-publisher-v2.schema.json \ schema/metaserver-game-publisher-v1.schema.json \ spec/metaserver-directory.md spec/metaserver-publisher.md \ THIRD_PARTY_NOTICES.md LICENSE "${output}/" @@ -115,7 +117,9 @@ jq -n \ fi checksum_files+=(atrinik-game-v1.binpb framing.json \ metaserver-directory-v1.json metaserver-game-publisher-v1.json \ + metaserver-classic-publisher-v2.json \ metaserver-publisher-v1.json metaserver-directory-v1.schema.json \ + metaserver-classic-publisher-v2.schema.json \ metaserver-game-publisher-v1.schema.json metaserver-directory.md \ metaserver-publisher.md "${directory_fixtures[@]}" sbom.cdx.json \ provenance.json THIRD_PARTY_NOTICES.md LICENSE) diff --git a/tools/validate.sh b/tools/validate.sh index 8ac3ce2..48081eb 100755 --- a/tools/validate.sh +++ b/tools/validate.sh @@ -17,6 +17,7 @@ go test -run '^$' -fuzz '^Fuzz' -fuzztime=2s ./framing go test -run '^$' -fuzz '^FuzzPublisher$' -fuzztime=2s ./metaserver go test -run '^$' -fuzz '^FuzzDirectoryJSON$' -fuzztime=2s ./metaserver go test -run '^$' -fuzz '^FuzzGamePublishJSON$' -fuzztime=2s ./metaserver +go test -run '^$' -fuzz '^FuzzClassicV2PublishJSON$' -fuzztime=2s ./metaserver cargo fmt --all --check cargo clippy --workspace --all-targets -- -D warnings @@ -33,7 +34,9 @@ jq empty \ fixtures/metaserver-directory-v1.json \ fixtures/metaserver-directory-v1/*.json \ fixtures/metaserver-game-publisher-v1.json \ + fixtures/metaserver-classic-publisher-v2.json \ fixtures/metaserver-publisher-v1.json \ + schema/metaserver-classic-publisher-v2.schema.json \ schema/metaserver-game-publisher-v1.schema.json \ schema/metaserver-directory-v1.schema.json \ provenance/reuse.json \ From 1d3a50b385e655051edb071a57f91509e47b366f Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Fri, 14 Aug 2026 02:47:42 +0000 Subject: [PATCH 2/6] fix(metaserver): close Classic v2 conformance gaps --- fixtures/metaserver-classic-publisher-v2.json | 350 ++++++++++++++---- metaserver/classic_publisher_test.go | 289 +++++++++++---- metaserver/publisher.go | 6 + metaserver/publisher_test.go | 4 + spec/metaserver-publisher.md | 19 +- 5 files changed, 537 insertions(+), 131 deletions(-) diff --git a/fixtures/metaserver-classic-publisher-v2.json b/fixtures/metaserver-classic-publisher-v2.json index 18c8841..db3412e 100644 --- a/fixtures/metaserver-classic-publisher-v2.json +++ b/fixtures/metaserver-classic-publisher-v2.json @@ -29,7 +29,15 @@ "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000000;expires=1800000300;nonce=\"202122232425262728292a2b2c2d2e2f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:Gr86A+BT7xYperp9yC1Dpg2+AJf20bcontFR6hMJvxY=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 101\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000000;expires=1800000300;nonce=\"202122232425262728292a2b2c2d2e2f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", "signature_base64": "hn4Vp7Yk9oAcsT/ZJa3JjtPNPkojidIirbvpkugwnUncptdh7TMSkOBesO2BNR2UhWvirP798Nz1YLopF7tHOw==", - "signature_header": "atrinik=:hn4Vp7Yk9oAcsT/ZJa3JjtPNPkojidIirbvpkugwnUncptdh7TMSkOBesO2BNR2UhWvirP798Nz1YLopF7tHOw==:" + "signature_header": "atrinik=:hn4Vp7Yk9oAcsT/ZJa3JjtPNPkojidIirbvpkugwnUncptdh7TMSkOBesO2BNR2UhWvirP798Nz1YLopF7tHOw==:", + "expected_state": { + "authenticated_presence": true, + "public_listing": true, + "public_fields_retained": true, + "rendezvous": "unauthenticated", + "public_authorization_active": false, + "private_server_control_retained": false + } }, { "name": "public-protected-endpoint", @@ -43,7 +51,15 @@ "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "signature_header": "atrinik=:1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==:" + "signature_header": "atrinik=:1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==:", + "expected_state": { + "authenticated_presence": true, + "public_listing": true, + "public_fields_retained": true, + "rendezvous": "proof-required", + "public_authorization_active": true, + "private_server_control_retained": false + } }, { "name": "private-open-endpoint", @@ -57,7 +73,15 @@ "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001200;expires=1800001500;nonce=\"404142434445464748494a4b4c4d4e4f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:vDE4NdX7UjFFd8FvQAw6DJRGcWwkSlDVPQeoJ6WFF4c=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 103\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001200;expires=1800001500;nonce=\"404142434445464748494a4b4c4d4e4f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", "signature_base64": "iqRbY+IrGsqLmv33cE2lxOd0cE3dRT70ER5I1W/R+0pqFzpITv4iYu5e0Zpu1GllDo1dRBx3btHG0qFiC/gaEA==", - "signature_header": "atrinik=:iqRbY+IrGsqLmv33cE2lxOd0cE3dRT70ER5I1W/R+0pqFzpITv4iYu5e0Zpu1GllDo1dRBx3btHG0qFiC/gaEA==:" + "signature_header": "atrinik=:iqRbY+IrGsqLmv33cE2lxOd0cE3dRT70ER5I1W/R+0pqFzpITv4iYu5e0Zpu1GllDo1dRBx3btHG0qFiC/gaEA==:", + "expected_state": { + "authenticated_presence": true, + "public_listing": false, + "public_fields_retained": false, + "rendezvous": "not-found", + "public_authorization_active": false, + "private_server_control_retained": true + } }, { "name": "private-protected-addressless", @@ -71,7 +95,43 @@ "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001800;expires=1800002100;nonce=\"505152535455565758595a5b5c5d5e5f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:s56rYrOnUoGOi8+rE1WX6ztHKCHd7r9kmt8S5DX6uco=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 104\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001800;expires=1800002100;nonce=\"505152535455565758595a5b5c5d5e5f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", "signature_base64": "09ZOuTpd+zu1VRmV+Hs12jYGF4bVyxbCgLfNJR0PjsZyDA5bMni8P+4Ni/pYqMs261lxZe43J3iJSiseBxs4ng==", - "signature_header": "atrinik=:09ZOuTpd+zu1VRmV+Hs12jYGF4bVyxbCgLfNJR0PjsZyDA5bMni8P+4Ni/pYqMs261lxZe43J3iJSiseBxs4ng==:" + "signature_header": "atrinik=:09ZOuTpd+zu1VRmV+Hs12jYGF4bVyxbCgLfNJR0PjsZyDA5bMni8P+4Ni/pYqMs261lxZe43J3iJSiseBxs4ng==:", + "expected_state": { + "authenticated_presence": true, + "public_listing": false, + "public_fields_retained": false, + "rendezvous": "not-found", + "public_authorization_active": false, + "private_server_control_retained": true + } + } + ], + "cross_profile_replay": [ + { + "name": "v1-at-v2", + "source_profile": "classic-v1", + "target_profile": "classic-v2", + "server_id": "51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b", + "certificate_der_base64": "MIIBRzCB76ADAgECAgEBMAoGCCqGSM49BAMCMCQxIjAgBgNVBAMTGUF0cmluaWsgcHVibGlzaGVyIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAkMSIwIAYDVQQDExlBdHJpbmlrIHB1Ymxpc2hlciBmaXh0dXJlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPQd9J89i9RHBKxJbWY3LVTOACf0pjFZgJTnCMy6BlQ27hfl0xwZf5LkuREpxVbe0xh6ydj5V00OY5Sh5myB4pqMSMBAwDgYDVR0PAQH/BAQDAgeAMAoGCCqGSM49BAMCA0cAMEQCIAbaCO4YENW0H67c1Gj292fvPwTwvwsQ8F5ObWMOv0wyAiArIt0mJkn917HvWQw8HrBVOCyXfGfocde6yjbjwSUcEA==", + "body": "{\"schema\":\"atrinik-classic-publish-v1\",\"serverId\":\"51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b\",\"certificate\":\"MIIBRzCB76ADAgECAgEBMAoGCCqGSM49BAMCMCQxIjAgBgNVBAMTGUF0cmluaWsgcHVibGlzaGVyIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAkMSIwIAYDVQQDExlBdHJpbmlrIHB1Ymxpc2hlciBmaXh0dXJlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPQd9J89i9RHBKxJbWY3LVTOACf0pjFZgJTnCMy6BlQ27hfl0xwZf5LkuREpxVbe0xh6ydj5V00OY5Sh5myB4pqMSMBAwDgYDVR0PAQH/BAQDAgeAMAoGCCqGSM49BAMCA0cAMEQCIAbaCO4YENW0H67c1Gj292fvPwTwvwsQ8F5ObWMOv0wyAiArIt0mJkn917HvWQw8HrBVOCyXfGfocde6yjbjwSUcEA==\",\"name\":\"Atrinik Classic\",\"playersCount\":3,\"version\":\"5.7.0\",\"textComment\":\"Fixture\",\"public\":true,\"passwordRequired\":true}", + "source_signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v1/classic/servers/51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b/publish\n\"content-digest\": sha-256=:7r1sfMoSYvvgYGEEOEHGcIKysWU8LtvUcNxXBdd/hyw=:\n\"content-type\": application/json\n\"atrinik-server-id\": 51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b\n\"atrinik-publish-sequence\": 18446744073709551610\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000000;expires=1800000300;nonce=\"000102030405060708090a0b0c0d0e0f\";alg=\"ecdsa-p256-sha256\";keyid=\"51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b\";tag=\"atrinik-classic-publish-v1\"", + "target_signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b/publish\n\"content-digest\": sha-256=:7r1sfMoSYvvgYGEEOEHGcIKysWU8LtvUcNxXBdd/hyw=:\n\"content-type\": application/json\n\"atrinik-server-id\": 51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b\n\"atrinik-publish-sequence\": 18446744073709551610\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000000;expires=1800000300;nonce=\"000102030405060708090a0b0c0d0e0f\";alg=\"ecdsa-p256-sha256\";keyid=\"51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "ec3TLCXjfy0FCqNAL/cdtrWSRj2ktJ2v81L7yuzgpw270zFhROANO0rUQ5y0hU5NSOfVai2chQlbNY9zHEffIw==", + "target_body_error": "unsupported_schema", + "target_signature_error": "invalid_signature" + }, + { + "name": "v2-at-v1", + "source_profile": "classic-v2", + "target_profile": "classic-v1", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "source_signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "target_signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v1/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v1\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "target_body_error": "unsupported_schema", + "target_signature_error": "invalid_signature" } ], "signature_negative": [ @@ -192,79 +252,245 @@ } ], "migration": { - "initial_profile": "classic-v1", - "high_water_sequence": "100", - "retained_nonces": [ - "000102030405060708090a0b0c0d0e0f" - ], "cases": [ - { - "name": "higher-v2-upgrade", - "starting_mode": "classic-v1", - "starting_high_water_sequence": "100", - "profile": "classic-v2", - "sequence": "101", - "nonce": "101112131415161718191a1b1c1d1e1f", - "accepted": true, - "upgrades_identity": true, - "minimum_next_sequence": "102" - }, { "name": "equal-v2-sequence", - "starting_mode": "classic-v1", - "starting_high_water_sequence": "100", - "profile": "classic-v2", - "sequence": "100", - "nonce": "202122232425262728292a2b2c2d2e2f", - "accepted": false, - "upgrades_identity": false, - "minimum_next_sequence": "101" + "before": { + "mode": "classic-v1", + "high_water_sequence": "100", + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "v1_listing_present": true, + "v1_rendezvous_generation_present": true, + "v1_controls_active": true, + "pending_authorizations": 2, + "tickets": 3, + "v2_usable": false + }, + "request": { + "profile": "classic-v2", + "sequence": "100", + "nonce": "202122232425262728292a2b2c2d2e2f" + }, + "expected": { + "accepted": false, + "error": "publish_replay", + "minimum_next_sequence": "101", + "state": { + "mode": "classic-v1", + "high_water_sequence": "100", + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "v1_listing_present": true, + "v1_rendezvous_generation_present": true, + "v1_controls_active": true, + "pending_authorizations": 2, + "tickets": 3, + "v2_usable": false + } + } }, { "name": "stale-v2-sequence", - "starting_mode": "classic-v1", - "starting_high_water_sequence": "100", - "profile": "classic-v2", - "sequence": "99", - "nonce": "303132333435363738393a3b3c3d3e3f", - "accepted": false, - "upgrades_identity": false, - "minimum_next_sequence": "101" + "before": { + "mode": "classic-v1", + "high_water_sequence": "100", + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "v1_listing_present": true, + "v1_rendezvous_generation_present": true, + "v1_controls_active": true, + "pending_authorizations": 2, + "tickets": 3, + "v2_usable": false + }, + "request": { + "profile": "classic-v2", + "sequence": "99", + "nonce": "303132333435363738393a3b3c3d3e3f" + }, + "expected": { + "accepted": false, + "error": "publish_replay", + "minimum_next_sequence": "101", + "state": { + "mode": "classic-v1", + "high_water_sequence": "100", + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "v1_listing_present": true, + "v1_rendezvous_generation_present": true, + "v1_controls_active": true, + "pending_authorizations": 2, + "tickets": 3, + "v2_usable": false + } + } }, { "name": "retained-v1-nonce-at-v2", - "starting_mode": "classic-v1", - "starting_high_water_sequence": "100", - "profile": "classic-v2", - "sequence": "101", - "nonce": "000102030405060708090a0b0c0d0e0f", - "accepted": false, - "upgrades_identity": false, - "minimum_next_sequence": "101" + "before": { + "mode": "classic-v1", + "high_water_sequence": "100", + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "v1_listing_present": true, + "v1_rendezvous_generation_present": true, + "v1_controls_active": true, + "pending_authorizations": 2, + "tickets": 3, + "v2_usable": false + }, + "request": { + "profile": "classic-v2", + "sequence": "101", + "nonce": "000102030405060708090a0b0c0d0e0f" + }, + "expected": { + "accepted": false, + "error": "publish_replay", + "minimum_next_sequence": "101", + "state": { + "mode": "classic-v1", + "high_water_sequence": "100", + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "v1_listing_present": true, + "v1_rendezvous_generation_present": true, + "v1_controls_active": true, + "pending_authorizations": 2, + "tickets": 3, + "v2_usable": false + } + } + }, + { + "name": "higher-v2-upgrade", + "before": { + "mode": "classic-v1", + "high_water_sequence": "100", + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "v1_listing_present": true, + "v1_rendezvous_generation_present": true, + "v1_controls_active": true, + "pending_authorizations": 2, + "tickets": 3, + "v2_usable": false + }, + "request": { + "profile": "classic-v2", + "sequence": "101", + "nonce": "101112131415161718191a1b1c1d1e1f" + }, + "expected": { + "accepted": true, + "minimum_next_sequence": "102", + "state": { + "mode": "classic-v2-only", + "high_water_sequence": "101", + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f", + "101112131415161718191a1b1c1d1e1f" + ], + "v1_listing_present": false, + "v1_rendezvous_generation_present": false, + "v1_controls_active": false, + "pending_authorizations": 0, + "tickets": 0, + "v2_usable": true + } + } }, { "name": "post-upgrade-v1", - "starting_mode": "classic-v2-only", - "starting_high_water_sequence": "101", - "profile": "classic-v1", - "sequence": "102", - "nonce": "404142434445464748494a4b4c4d4e4f", - "accepted": false, - "upgrades_identity": false, - "minimum_next_sequence": "102" + "before": { + "mode": "classic-v2-only", + "high_water_sequence": "101", + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f", + "101112131415161718191a1b1c1d1e1f" + ], + "v1_listing_present": false, + "v1_rendezvous_generation_present": false, + "v1_controls_active": false, + "pending_authorizations": 0, + "tickets": 0, + "v2_usable": true + }, + "request": { + "profile": "classic-v1", + "sequence": "102", + "nonce": "404142434445464748494a4b4c4d4e4f" + }, + "expected": { + "accepted": false, + "error": "profile_retired", + "minimum_next_sequence": "102", + "state": { + "mode": "classic-v2-only", + "high_water_sequence": "101", + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f", + "101112131415161718191a1b1c1d1e1f" + ], + "v1_listing_present": false, + "v1_rendezvous_generation_present": false, + "v1_controls_active": false, + "pending_authorizations": 0, + "tickets": 0, + "v2_usable": true + } + } }, { "name": "next-v2", - "starting_mode": "classic-v2-only", - "starting_high_water_sequence": "101", - "profile": "classic-v2", - "sequence": "102", - "nonce": "505152535455565758595a5b5c5d5e5f", - "accepted": true, - "upgrades_identity": false, - "minimum_next_sequence": "103" + "before": { + "mode": "classic-v2-only", + "high_water_sequence": "101", + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f", + "101112131415161718191a1b1c1d1e1f" + ], + "v1_listing_present": false, + "v1_rendezvous_generation_present": false, + "v1_controls_active": false, + "pending_authorizations": 0, + "tickets": 0, + "v2_usable": true + }, + "request": { + "profile": "classic-v2", + "sequence": "102", + "nonce": "505152535455565758595a5b5c5d5e5f" + }, + "expected": { + "accepted": true, + "minimum_next_sequence": "103", + "state": { + "mode": "classic-v2-only", + "high_water_sequence": "102", + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f", + "101112131415161718191a1b1c1d1e1f", + "505152535455565758595a5b5c5d5e5f" + ], + "v1_listing_present": false, + "v1_rendezvous_generation_present": false, + "v1_controls_active": false, + "pending_authorizations": 0, + "tickets": 0, + "v2_usable": true + } + } } - ], - "post_upgrade_mode": "classic-v2-only" + ] } } diff --git a/metaserver/classic_publisher_test.go b/metaserver/classic_publisher_test.go index 5064933..6baa252 100644 --- a/metaserver/classic_publisher_test.go +++ b/metaserver/classic_publisher_test.go @@ -10,6 +10,7 @@ import ( "encoding/json" "errors" "os" + "reflect" "strconv" "strings" "testing" @@ -18,18 +19,28 @@ import ( ) type classicV2PositiveFixture struct { - Name string `json:"name"` - Sequence string `json:"sequence"` - Nonce string `json:"nonce"` - Created int64 `json:"created"` - Expires int64 `json:"expires"` - Body string `json:"body"` - ContentDigest string `json:"content_digest"` - Path string `json:"path"` - SignatureInput string `json:"signature_input"` - SignatureBase string `json:"signature_base"` - SignatureBase64 string `json:"signature_base64"` - SignatureHeader string `json:"signature_header"` + Name string `json:"name"` + Sequence string `json:"sequence"` + Nonce string `json:"nonce"` + Created int64 `json:"created"` + Expires int64 `json:"expires"` + Body string `json:"body"` + ContentDigest string `json:"content_digest"` + Path string `json:"path"` + SignatureInput string `json:"signature_input"` + SignatureBase string `json:"signature_base"` + SignatureBase64 string `json:"signature_base64"` + SignatureHeader string `json:"signature_header"` + ExpectedState classicV2PublicationState `json:"expected_state"` +} + +type classicV2PublicationState struct { + AuthenticatedPresence bool `json:"authenticated_presence"` + PublicListing bool `json:"public_listing"` + PublicFieldsRetained bool `json:"public_fields_retained"` + Rendezvous string `json:"rendezvous"` + PublicAuthorizationActive bool `json:"public_authorization_active"` + PrivateServerControlRetained bool `json:"private_server_control_retained"` } type classicV2NegativeFixture struct { @@ -46,16 +57,50 @@ type classicV2SignatureNegativeFixture struct { Error string `json:"error"` } +type classicV2CrossProfileReplayFixture struct { + Name string `json:"name"` + SourceProfile string `json:"source_profile"` + TargetProfile string `json:"target_profile"` + ServerID string `json:"server_id"` + CertificateDERBase64 string `json:"certificate_der_base64"` + Body string `json:"body"` + SourceSignatureBase string `json:"source_signature_base"` + TargetSignatureBase string `json:"target_signature_base"` + SignatureBase64 string `json:"signature_base64"` + TargetBodyError string `json:"target_body_error"` + TargetSignatureError string `json:"target_signature_error"` +} + +type classicV2MigrationState struct { + Mode string `json:"mode"` + HighWaterSequence string `json:"high_water_sequence"` + RetainedNonces []string `json:"retained_nonces"` + V1ListingPresent bool `json:"v1_listing_present"` + V1RendezvousGenerationPresent bool `json:"v1_rendezvous_generation_present"` + V1ControlsActive bool `json:"v1_controls_active"` + PendingAuthorizations int `json:"pending_authorizations"` + Tickets int `json:"tickets"` + V2Usable bool `json:"v2_usable"` +} + +type classicV2MigrationRequest struct { + Profile string `json:"profile"` + Sequence string `json:"sequence"` + Nonce string `json:"nonce"` +} + +type classicV2MigrationExpected struct { + Accepted bool `json:"accepted"` + Error string `json:"error"` + MinimumNextSequence string `json:"minimum_next_sequence"` + State classicV2MigrationState `json:"state"` +} + type classicV2MigrationCase struct { - Name string `json:"name"` - StartingMode string `json:"starting_mode"` - StartingHighWater string `json:"starting_high_water_sequence"` - Profile string `json:"profile"` - Sequence string `json:"sequence"` - Nonce string `json:"nonce"` - Accepted bool `json:"accepted"` - UpgradesIdentity bool `json:"upgrades_identity"` - MinimumNextSequence string `json:"minimum_next_sequence"` + Name string `json:"name"` + Before classicV2MigrationState `json:"before"` + Request classicV2MigrationRequest `json:"request"` + Expected classicV2MigrationExpected `json:"expected"` } type classicV2Fixture struct { @@ -76,15 +121,12 @@ type classicV2Fixture struct { HostnameBytes int `json:"hostname_bytes"` Port uint32 `json:"port"` } `json:"maximum_bounds"` - Positive []classicV2PositiveFixture `json:"positive"` - SignatureNegative []classicV2SignatureNegativeFixture `json:"signature_negative"` - Negative []classicV2NegativeFixture `json:"negative"` - Migration struct { - InitialProfile string `json:"initial_profile"` - HighWater string `json:"high_water_sequence"` - RetainedNonces []string `json:"retained_nonces"` - Cases []classicV2MigrationCase `json:"cases"` - PostUpgradeMode string `json:"post_upgrade_mode"` + Positive []classicV2PositiveFixture `json:"positive"` + SignatureNegative []classicV2SignatureNegativeFixture `json:"signature_negative"` + Negative []classicV2NegativeFixture `json:"negative"` + CrossProfileReplay []classicV2CrossProfileReplayFixture `json:"cross_profile_replay"` + Migration struct { + Cases []classicV2MigrationCase `json:"cases"` } `json:"migration"` } @@ -172,6 +214,24 @@ func TestClassicV2PublisherGoldenFixtures(t *testing.T) { ); err != nil { t.Fatalf("fixture signature did not verify: %v", err) } + wantRendezvous := "not-found" + if request.Public { + wantRendezvous = "unauthenticated" + if request.AccessCodeRequired { + wantRendezvous = "proof-required" + } + } + wantState := classicV2PublicationState{ + AuthenticatedPresence: true, + PublicListing: request.Public, + PublicFieldsRetained: request.Public, + Rendezvous: wantRendezvous, + PublicAuthorizationActive: request.Public && request.AccessCodeRequired, + PrivateServerControlRetained: !request.Public, + } + if test.ExpectedState != wantState { + t.Fatalf("publication state %#v, want %#v", test.ExpectedState, wantState) + } for _, profile := range []metaserver.Profile{ metaserver.ClassicV1Profile, @@ -210,6 +270,70 @@ func TestClassicV2PublisherGoldenFixtures(t *testing.T) { } } +func TestClassicV2CrossProfileReplayFixtures(t *testing.T) { + fixture := loadClassicV2Fixture(t) + if len(fixture.CrossProfileReplay) != 2 { + t.Fatalf("cross-profile replay count %d, want 2", len(fixture.CrossProfileReplay)) + } + v1 := loadPublisherFixture(t) + seen := make(map[string]bool) + for _, test := range fixture.CrossProfileReplay { + t.Run(test.Name, func(t *testing.T) { + if test.Name != strings.TrimPrefix(test.SourceProfile, "classic-")+"-at-"+strings.TrimPrefix(test.TargetProfile, "classic-") || + test.TargetBodyError != "unsupported_schema" || + test.TargetSignatureError != "invalid_signature" { + t.Fatal("cross-profile replay metadata is incomplete") + } + certificate := decodeBase64(t, test.CertificateDERBase64) + signature := decodeBase64(t, test.SignatureBase64) + if err := metaserver.VerifyCertificateSignature(certificate, test.ServerID, test.SourceSignatureBase, signature); err != nil { + t.Fatalf("source signature did not verify: %v", err) + } + if err := metaserver.VerifyCertificateSignature(certificate, test.ServerID, test.TargetSignatureBase, signature); !errors.Is(err, metaserver.ErrInvalidSignature) { + t.Fatalf("cross-profile signature verified: %v", err) + } + + var envelope struct { + Schema string `json:"schema"` + } + if err := json.Unmarshal([]byte(test.Body), &envelope); err != nil { + t.Fatal(err) + } + switch test.SourceProfile { + case "classic-v1": + if test.TargetProfile != "classic-v2" || test.Body != v1.Body || test.ServerID != v1.ServerID || + test.CertificateDERBase64 != v1.CertificateDERBase64 || test.SourceSignatureBase != v1.SignatureBase || + test.SignatureBase64 != v1.SignatureBase64 || envelope.Schema != "atrinik-classic-publish-v1" { + t.Fatal("v1-at-v2 vector is not anchored to the frozen v1 golden") + } + if _, err := metaserver.ParseClassicV2PublishJSON([]byte(test.Body)); classicPublishErrorCode(t, err) != metaserver.ClassicPublishUnsupportedSchema { + t.Fatalf("v1 body was not rejected by the v2 codec: %v", err) + } + case "classic-v2": + if test.TargetProfile != "classic-v1" || envelope.Schema != "atrinik-classic-publish-v2" { + t.Fatal("v2-at-v1 vector does not target the frozen v1 domain") + } + if _, err := metaserver.ParseClassicV2PublishJSON([]byte(test.Body)); err != nil { + t.Fatalf("v2 replay source is not a valid v2 body: %v", err) + } + anchored := false + for _, positive := range fixture.Positive { + anchored = anchored || (test.Body == positive.Body && test.SourceSignatureBase == positive.SignatureBase && test.SignatureBase64 == positive.SignatureBase64) + } + if !anchored { + t.Fatal("v2-at-v1 vector is not anchored to a v2 golden") + } + default: + t.Fatalf("unknown source profile %q", test.SourceProfile) + } + seen[test.SourceProfile+"/"+test.TargetProfile] = true + }) + } + if !seen["classic-v1/classic-v2"] || !seen["classic-v2/classic-v1"] { + t.Fatal("cross-profile replay fixture is not bidirectional") + } +} + func TestClassicV2PublisherNegativeFixtures(t *testing.T) { fixture := loadClassicV2Fixture(t) if len(fixture.Negative) < 12 { @@ -231,48 +355,79 @@ func TestClassicV2PublisherNegativeFixtures(t *testing.T) { func TestClassicV2MigrationFixture(t *testing.T) { fixture := loadClassicV2Fixture(t) - if fixture.Migration.InitialProfile != "classic-v1" || - fixture.Migration.HighWater != "100" || - len(fixture.Migration.RetainedNonces) == 0 || - fixture.Migration.PostUpgradeMode != "classic-v2-only" { - t.Fatal("migration fixture lacks the frozen v1 starting state or irreversible v2 result") - } - want := map[string]struct { - starting string - highWater string - accepted bool - upgrades bool - minimum string - }{ - "higher-v2-upgrade": {"classic-v1", "100", true, true, "102"}, - "equal-v2-sequence": {"classic-v1", "100", false, false, "101"}, - "stale-v2-sequence": {"classic-v1", "100", false, false, "101"}, - "retained-v1-nonce-at-v2": {"classic-v1", "100", false, false, "101"}, - "post-upgrade-v1": {"classic-v2-only", "101", false, false, "102"}, - "next-v2": {"classic-v2-only", "101", true, false, "103"}, - } - if len(fixture.Migration.Cases) != len(want) { - t.Fatalf("migration case count %d, want %d", len(fixture.Migration.Cases), len(want)) + if len(fixture.Migration.Cases) != 6 { + t.Fatalf("migration case count %d, want 6", len(fixture.Migration.Cases)) } for _, test := range fixture.Migration.Cases { - expected, ok := want[test.Name] - if !ok || test.StartingMode != expected.starting || - test.StartingHighWater != expected.highWater || - test.Accepted != expected.accepted || - test.UpgradesIdentity != expected.upgrades || - test.MinimumNextSequence != expected.minimum { - t.Fatalf("migration case %q differs from the declared lineage", test.Name) - } - if _, err := strconv.ParseUint(test.StartingHighWater, 10, 64); err != nil { - t.Fatalf("migration case %q has invalid starting high water: %v", test.Name, err) - } - if _, err := strconv.ParseUint(test.Sequence, 10, 64); err != nil { - t.Fatalf("migration case %q has invalid sequence: %v", test.Name, err) + t.Run(test.Name, func(t *testing.T) { + actual := applyClassicV2MigrationFixture(t, test.Before, test.Request) + if !reflect.DeepEqual(actual, test.Expected) { + t.Fatalf("transition result\n got: %#v\nwant: %#v", actual, test.Expected) + } + if !actual.Accepted && !reflect.DeepEqual(actual.State, test.Before) { + t.Fatal("rejected migration mutated replay or publication state") + } + }) + } +} + +func applyClassicV2MigrationFixture(t *testing.T, before classicV2MigrationState, request classicV2MigrationRequest) classicV2MigrationExpected { + t.Helper() + highWater, err := strconv.ParseUint(before.HighWaterSequence, 10, 64) + if err != nil { + t.Fatalf("invalid high-water sequence: %v", err) + } + sequence, err := strconv.ParseUint(request.Sequence, 10, 64) + if err != nil { + t.Fatalf("invalid request sequence: %v", err) + } + if request.Profile != "classic-v1" && request.Profile != "classic-v2" { + t.Fatalf("unknown request profile %q", request.Profile) + } + if before.Mode != "classic-v1" && before.Mode != "classic-v2-only" { + t.Fatalf("unknown migration mode %q", before.Mode) + } + for _, nonce := range append(append([]string(nil), before.RetainedNonces...), request.Nonce) { + decoded, err := hex.DecodeString(nonce) + if err != nil || len(decoded) != 16 { + t.Fatalf("invalid retained or request nonce %q", nonce) } - if nonce, err := hex.DecodeString(test.Nonce); err != nil || len(nonce) != 16 { - t.Fatalf("migration case %q has invalid nonce", test.Name) + } + + result := classicV2MigrationExpected{ + MinimumNextSequence: strconv.FormatUint(highWater+1, 10), + State: before, + } + result.State.RetainedNonces = append([]string(nil), before.RetainedNonces...) + if before.Mode == "classic-v2-only" && request.Profile == "classic-v1" { + result.Error = "profile_retired" + return result + } + for _, nonce := range before.RetainedNonces { + if nonce == request.Nonce { + result.Error = "publish_replay" + return result } } + if sequence <= highWater { + result.Error = "publish_replay" + return result + } + + result.Accepted = true + result.MinimumNextSequence = strconv.FormatUint(sequence+1, 10) + result.State.HighWaterSequence = request.Sequence + result.State.RetainedNonces = append(result.State.RetainedNonces, request.Nonce) + if request.Profile == "classic-v2" { + result.State.Mode = "classic-v2-only" + result.State.V1ListingPresent = false + result.State.V1RendezvousGenerationPresent = false + result.State.V1ControlsActive = false + result.State.PendingAuthorizations = 0 + result.State.Tickets = 0 + result.State.V2Usable = true + } + return result } func TestClassicV2PublisherValidationClasses(t *testing.T) { diff --git a/metaserver/publisher.go b/metaserver/publisher.go index d572ddd..df99378 100644 --- a/metaserver/publisher.go +++ b/metaserver/publisher.go @@ -58,6 +58,12 @@ const ( ClassicV1Profile Profile = 1 GameProfile Profile = 2 ClassicV2Profile Profile = 3 + + // ClassicProfile is the deprecated name of the frozen Classic v1 profile. + // New code must select ClassicV1Profile or ClassicV2Profile explicitly. + ClassicProfile = ClassicV1Profile + // ClassicSignatureTag is the deprecated name of the frozen Classic v1 tag. + ClassicSignatureTag = ClassicV1SignatureTag ) // Parameters are the bounded per-request values carried by Signature-Input diff --git a/metaserver/publisher_test.go b/metaserver/publisher_test.go index 9a4d217..27cef5f 100644 --- a/metaserver/publisher_test.go +++ b/metaserver/publisher_test.go @@ -46,6 +46,10 @@ type publisherFixture struct { } func TestPublisherGoldenFixture(t *testing.T) { + if metaserver.ClassicProfile != metaserver.ClassicV1Profile || + metaserver.ClassicSignatureTag != metaserver.ClassicV1SignatureTag { + t.Fatal("deprecated Classic v1 API aliases changed value") + } fixture := loadPublisherFixture(t) parameters := fixtureParameters(t, fixture, metaserver.ClassicV1Profile) components, err := metaserver.Build(parameters, []byte(fixture.Body)) diff --git a/spec/metaserver-publisher.md b/spec/metaserver-publisher.md index 85aabbf..573f3a0 100644 --- a/spec/metaserver-publisher.md +++ b/spec/metaserver-publisher.md @@ -105,6 +105,19 @@ proof, candidate, password, or authentication result is publisher data. There is no password-only, invite-only, join-only, partially protected, or hybrid v2 body. +For both Classic versions, `public: true` makes the complete authenticated +model eligible for its version's public directory and rendezvous behavior. +`public: false` still advances the identity's replay and authenticated +presence state, but the receiver atomically removes any public directory +entry, public endpoint/display projection, public rendezvous room generation, +pending client authorization, candidate, and ticket state for that identity. +The public rendezvous route then returns the same fixed not-found result as an +absent identity. Authenticated owner, replay, presence, and server-control +state may remain private, but no display, endpoint, or access-policy field is +retained as public directory state. A later public body must republish the +complete model. Private publication never makes possession of an access code a +discovery grant. + The normative schema is `schema/metaserver-classic-publisher-v2.schema.json`. The byte-identical open, protected, public, private, endpoint-present, and endpoint-absent bodies, @@ -208,8 +221,10 @@ atomically beside the persistent server identity and included in backup/restore policy. Only after certificate and signature verification may a receiver charge an -identity quota or inspect the body as authoritative input. For each server -identity and its selected profile, one serialized atomic publication +identity quota or inspect the body as authoritative input. Replay state is +partitioned into explicit lineages: Classic v1 and v2 share one lineage per +server identity, while Game Protocol 1 has its own independent lineage. For +each server identity and replay lineage, one serialized atomic publication operation: 1. rejects a nonce already present in the bounded replay window; From 7c33c336a88becf4219676b89f61baf06a2213ed Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Fri, 14 Aug 2026 02:56:51 +0000 Subject: [PATCH 3/6] fix(metaserver): make retirement fixtures exhaustive --- fixtures/metaserver-classic-publisher-v2.json | 878 ++++++++++++------ metaserver/classic_publisher_test.go | 151 ++- spec/metaserver-publisher.md | 35 +- 3 files changed, 736 insertions(+), 328 deletions(-) diff --git a/fixtures/metaserver-classic-publisher-v2.json b/fixtures/metaserver-classic-publisher-v2.json index db3412e..78be0e9 100644 --- a/fixtures/metaserver-classic-publisher-v2.json +++ b/fixtures/metaserver-classic-publisher-v2.json @@ -1,111 +1,7 @@ { - "fixture_version": 1, - "profile": "classic-v2", "authority": "publish.meta.atrinik.org", - "method": "POST", - "content_type": "application/json", - "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", - "maximum_bounds": { - "body_bytes": 4096, - "certificate_der_bytes": 2048, - "name_utf8_bytes": 80, - "players_count": 4294967295, - "version_utf8_bytes": 32, - "text_comment_utf8_bytes": 256, - "hostname_bytes": 253, - "port": 65535 - }, - "positive": [ - { - "name": "public-open-addressless", - "sequence": "101", - "nonce": "202122232425262728292a2b2c2d2e2f", - "created": 1800000000, - "expires": 1800000300, - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":3,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":false}", - "content_digest": "sha-256=:Gr86A+BT7xYperp9yC1Dpg2+AJf20bcontFR6hMJvxY=:", - "path": "/v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish", - "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000000;expires=1800000300;nonce=\"202122232425262728292a2b2c2d2e2f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:Gr86A+BT7xYperp9yC1Dpg2+AJf20bcontFR6hMJvxY=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 101\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000000;expires=1800000300;nonce=\"202122232425262728292a2b2c2d2e2f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base64": "hn4Vp7Yk9oAcsT/ZJa3JjtPNPkojidIirbvpkugwnUncptdh7TMSkOBesO2BNR2UhWvirP798Nz1YLopF7tHOw==", - "signature_header": "atrinik=:hn4Vp7Yk9oAcsT/ZJa3JjtPNPkojidIirbvpkugwnUncptdh7TMSkOBesO2BNR2UhWvirP798Nz1YLopF7tHOw==:", - "expected_state": { - "authenticated_presence": true, - "public_listing": true, - "public_fields_retained": true, - "rendezvous": "unauthenticated", - "public_authorization_active": false, - "private_server_control_retained": false - } - }, - { - "name": "public-protected-endpoint", - "sequence": "102", - "nonce": "303132333435363738393a3b3c3d3e3f", - "created": 1800000600, - "expires": 1800000900, - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "content_digest": "sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:", - "path": "/v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish", - "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "signature_header": "atrinik=:1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==:", - "expected_state": { - "authenticated_presence": true, - "public_listing": true, - "public_fields_retained": true, - "rendezvous": "proof-required", - "public_authorization_active": true, - "private_server_control_retained": false - } - }, - { - "name": "private-open-endpoint", - "sequence": "103", - "nonce": "404142434445464748494a4b4c4d4e4f", - "created": 1800001200, - "expires": 1800001500, - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":5,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":false,\"accessCodeRequired\":false,\"hostname\":\"private.example.org\",\"port\":13328}", - "content_digest": "sha-256=:vDE4NdX7UjFFd8FvQAw6DJRGcWwkSlDVPQeoJ6WFF4c=:", - "path": "/v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish", - "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001200;expires=1800001500;nonce=\"404142434445464748494a4b4c4d4e4f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:vDE4NdX7UjFFd8FvQAw6DJRGcWwkSlDVPQeoJ6WFF4c=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 103\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001200;expires=1800001500;nonce=\"404142434445464748494a4b4c4d4e4f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base64": "iqRbY+IrGsqLmv33cE2lxOd0cE3dRT70ER5I1W/R+0pqFzpITv4iYu5e0Zpu1GllDo1dRBx3btHG0qFiC/gaEA==", - "signature_header": "atrinik=:iqRbY+IrGsqLmv33cE2lxOd0cE3dRT70ER5I1W/R+0pqFzpITv4iYu5e0Zpu1GllDo1dRBx3btHG0qFiC/gaEA==:", - "expected_state": { - "authenticated_presence": true, - "public_listing": false, - "public_fields_retained": false, - "rendezvous": "not-found", - "public_authorization_active": false, - "private_server_control_retained": true - } - }, - { - "name": "private-protected-addressless", - "sequence": "104", - "nonce": "505152535455565758595a5b5c5d5e5f", - "created": 1800001800, - "expires": 1800002100, - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":6,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":false,\"accessCodeRequired\":true}", - "content_digest": "sha-256=:s56rYrOnUoGOi8+rE1WX6ztHKCHd7r9kmt8S5DX6uco=:", - "path": "/v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish", - "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001800;expires=1800002100;nonce=\"505152535455565758595a5b5c5d5e5f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:s56rYrOnUoGOi8+rE1WX6ztHKCHd7r9kmt8S5DX6uco=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 104\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001800;expires=1800002100;nonce=\"505152535455565758595a5b5c5d5e5f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base64": "09ZOuTpd+zu1VRmV+Hs12jYGF4bVyxbCgLfNJR0PjsZyDA5bMni8P+4Ni/pYqMs261lxZe43J3iJSiseBxs4ng==", - "signature_header": "atrinik=:09ZOuTpd+zu1VRmV+Hs12jYGF4bVyxbCgLfNJR0PjsZyDA5bMni8P+4Ni/pYqMs261lxZe43J3iJSiseBxs4ng==:", - "expected_state": { - "authenticated_presence": true, - "public_listing": false, - "public_fields_retained": false, - "rendezvous": "not-found", - "public_authorization_active": false, - "private_server_control_retained": true - } - } - ], + "content_type": "application/json", "cross_profile_replay": [ { "name": "v1-at-v2", @@ -134,363 +30,743 @@ "target_signature_error": "invalid_signature" } ], - "signature_negative": [ - { - "name": "v2-signature-at-v1", - "positive": "public-protected-endpoint", - "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v1/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v1\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "error": "invalid_signature" - }, - { - "name": "v2-signature-at-game", - "positive": "public-protected-endpoint", - "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v1/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-game-publish-v1\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "error": "invalid_signature" - }, - { - "name": "wrong-v2-route", - "positive": "public-protected-endpoint", - "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/server/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "error": "invalid_signature" - }, - { - "name": "wrong-v2-tag", - "positive": "public-protected-endpoint", - "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v3\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "error": "invalid_signature" - } - ], - "negative": [ - { - "name": "v1-schema", - "body": "{\"schema\":\"atrinik-classic-publish-v1\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "unsupported_schema" - }, - { - "name": "legacy-password-field", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"passwordRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "noncanonical_json" - }, - { - "name": "wrong-key-order", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"playersCount\":4,\"name\":\"Atrinik Classic\",\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "noncanonical_json" - }, - { - "name": "whitespace", - "body": "{ \"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "noncanonical_json" - }, - { - "name": "duplicate-field", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "noncanonical_json" - }, - { - "name": "unknown-field", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327,\"extra\":true}", - "error": "noncanonical_json" - }, - { - "name": "malformed-boolean", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":\"true\",\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "invalid_json" - }, - { - "name": "missing-policy", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "noncanonical_json" - }, - { - "name": "hostname-without-port", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\"}", - "error": "invalid_endpoint" - }, - { - "name": "port-without-hostname", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"port\":13327}", - "error": "invalid_endpoint" - }, - { - "name": "uppercase-server-id", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13E46C3B910727029646BA5DB7EE5930080F832AF4D9E29F70CF781250A23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "invalid_identity" - }, - { - "name": "noncanonical-certificate", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ=\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "invalid_certificate" - }, - { - "name": "players-overflow", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4294967296,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "invalid_players" - }, - { - "name": "numeric-hostname", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"192.0.2.1\",\"port\":13327}", - "error": "invalid_endpoint" + "fixture_version": 1, + "global_v1_retirement": { + "activation": { + "kind": "authorized-operator-transaction", + "prerequisite": "human-accepted-v5-canaries-and-one-way-alias-cutover" }, - { - "name": "uppercase-hostname", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"Play.example.org\",\"port\":13327}", - "error": "invalid_endpoint" + "before": { + "in_flight_v1_commits": 0, + "mode": "classic-v1-accepting", + "pending_authorizations": 3, + "tickets": 4, + "v1_authenticated_presences": 2, + "v1_controls": 2, + "v1_listings": 2, + "v1_rendezvous_generations": 1, + "v2_state_unchanged": true }, - { - "name": "control-text", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"bad\\nvalue\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "invalid_text" + "expected_after": { + "in_flight_v1_commits": 0, + "mode": "classic-v1-retired", + "pending_authorizations": 0, + "rollback_allowed": false, + "tickets": 0, + "v1_authenticated_presences": 0, + "v1_controls": 0, + "v1_listings": 0, + "v1_rendezvous_generations": 0, + "v2_state_unchanged": true }, - { - "name": "trailing-input", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}{}", - "error": "noncanonical_json" + "rejected_publish": { + "body": "{\"error\":{\"code\":\"profile_retired\"}}", + "cache_control": "no-store", + "http_status": 410, + "inspection": "none", + "minimum_next_sequence": "", + "profile": "classic-v1", + "sequence_or_nonce_consumed": false } - ], + }, + "maximum_bounds": { + "body_bytes": 4096, + "certificate_der_bytes": 2048, + "name_utf8_bytes": 80, + "players_count": 4294967295, + "version_utf8_bytes": 32, + "text_comment_utf8_bytes": 256, + "hostname_bytes": 253, + "port": 65535 + }, + "method": "POST", "migration": { "cases": [ { - "name": "equal-v2-sequence", "before": { - "mode": "classic-v1", "high_water_sequence": "100", + "mode": "classic-v1", + "pending_authorizations": 2, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" ], + "tickets": 3, + "v1_authenticated_presence": true, + "v1_controls_active": true, "v1_listing_present": true, "v1_rendezvous_generation_present": true, - "v1_controls_active": true, - "pending_authorizations": 2, - "tickets": 3, "v2_usable": false }, - "request": { - "profile": "classic-v2", - "sequence": "100", - "nonce": "202122232425262728292a2b2c2d2e2f" - }, "expected": { "accepted": false, "error": "publish_replay", "minimum_next_sequence": "101", "state": { - "mode": "classic-v1", "high_water_sequence": "100", + "mode": "classic-v1", + "pending_authorizations": 2, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" ], + "tickets": 3, + "v1_authenticated_presence": true, + "v1_controls_active": true, "v1_listing_present": true, "v1_rendezvous_generation_present": true, - "v1_controls_active": true, - "pending_authorizations": 2, - "tickets": 3, "v2_usable": false } + }, + "name": "equal-v2-sequence", + "request": { + "nonce": "202122232425262728292a2b2c2d2e2f", + "profile": "classic-v2", + "sequence": "100" } }, { - "name": "stale-v2-sequence", "before": { - "mode": "classic-v1", "high_water_sequence": "100", + "mode": "classic-v1", + "pending_authorizations": 2, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" ], + "tickets": 3, + "v1_authenticated_presence": true, + "v1_controls_active": true, "v1_listing_present": true, "v1_rendezvous_generation_present": true, - "v1_controls_active": true, - "pending_authorizations": 2, - "tickets": 3, "v2_usable": false }, - "request": { - "profile": "classic-v2", - "sequence": "99", - "nonce": "303132333435363738393a3b3c3d3e3f" - }, "expected": { "accepted": false, "error": "publish_replay", "minimum_next_sequence": "101", "state": { - "mode": "classic-v1", "high_water_sequence": "100", + "mode": "classic-v1", + "pending_authorizations": 2, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" ], + "tickets": 3, + "v1_authenticated_presence": true, + "v1_controls_active": true, "v1_listing_present": true, "v1_rendezvous_generation_present": true, - "v1_controls_active": true, - "pending_authorizations": 2, - "tickets": 3, "v2_usable": false } + }, + "name": "stale-v2-sequence", + "request": { + "nonce": "303132333435363738393a3b3c3d3e3f", + "profile": "classic-v2", + "sequence": "99" } }, { - "name": "retained-v1-nonce-at-v2", "before": { - "mode": "classic-v1", "high_water_sequence": "100", + "mode": "classic-v1", + "pending_authorizations": 2, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" ], + "tickets": 3, + "v1_authenticated_presence": true, + "v1_controls_active": true, "v1_listing_present": true, "v1_rendezvous_generation_present": true, - "v1_controls_active": true, - "pending_authorizations": 2, - "tickets": 3, "v2_usable": false }, - "request": { - "profile": "classic-v2", - "sequence": "101", - "nonce": "000102030405060708090a0b0c0d0e0f" - }, "expected": { "accepted": false, "error": "publish_replay", "minimum_next_sequence": "101", "state": { - "mode": "classic-v1", "high_water_sequence": "100", + "mode": "classic-v1", + "pending_authorizations": 2, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" ], + "tickets": 3, + "v1_authenticated_presence": true, + "v1_controls_active": true, "v1_listing_present": true, "v1_rendezvous_generation_present": true, - "v1_controls_active": true, - "pending_authorizations": 2, - "tickets": 3, "v2_usable": false } + }, + "name": "retained-v1-nonce-at-v2", + "request": { + "nonce": "000102030405060708090a0b0c0d0e0f", + "profile": "classic-v2", + "sequence": "101" } }, { - "name": "higher-v2-upgrade", "before": { - "mode": "classic-v1", "high_water_sequence": "100", + "mode": "classic-v1", + "pending_authorizations": 2, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" ], + "tickets": 3, + "v1_authenticated_presence": true, + "v1_controls_active": true, "v1_listing_present": true, "v1_rendezvous_generation_present": true, - "v1_controls_active": true, - "pending_authorizations": 2, - "tickets": 3, "v2_usable": false }, - "request": { - "profile": "classic-v2", - "sequence": "101", - "nonce": "101112131415161718191a1b1c1d1e1f" - }, "expected": { "accepted": true, "minimum_next_sequence": "102", "state": { - "mode": "classic-v2-only", "high_water_sequence": "101", + "mode": "classic-v2-only", + "pending_authorizations": 0, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f", "101112131415161718191a1b1c1d1e1f" ], + "tickets": 0, + "v1_authenticated_presence": false, + "v1_controls_active": false, "v1_listing_present": false, "v1_rendezvous_generation_present": false, - "v1_controls_active": false, - "pending_authorizations": 0, - "tickets": 0, "v2_usable": true } + }, + "name": "higher-v2-upgrade", + "request": { + "nonce": "101112131415161718191a1b1c1d1e1f", + "profile": "classic-v2", + "sequence": "101" } }, { - "name": "post-upgrade-v1", "before": { - "mode": "classic-v2-only", "high_water_sequence": "101", + "mode": "classic-v2-only", + "pending_authorizations": 0, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f", "101112131415161718191a1b1c1d1e1f" ], + "tickets": 0, + "v1_authenticated_presence": false, + "v1_controls_active": false, "v1_listing_present": false, "v1_rendezvous_generation_present": false, - "v1_controls_active": false, - "pending_authorizations": 0, - "tickets": 0, "v2_usable": true }, - "request": { - "profile": "classic-v1", - "sequence": "102", - "nonce": "404142434445464748494a4b4c4d4e4f" - }, "expected": { "accepted": false, "error": "profile_retired", - "minimum_next_sequence": "102", + "minimum_next_sequence": "", "state": { - "mode": "classic-v2-only", "high_water_sequence": "101", + "mode": "classic-v2-only", + "pending_authorizations": 0, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f", "101112131415161718191a1b1c1d1e1f" ], + "tickets": 0, + "v1_authenticated_presence": false, + "v1_controls_active": false, "v1_listing_present": false, "v1_rendezvous_generation_present": false, - "v1_controls_active": false, - "pending_authorizations": 0, - "tickets": 0, "v2_usable": true } + }, + "name": "post-upgrade-v1", + "request": { + "nonce": "404142434445464748494a4b4c4d4e4f", + "profile": "classic-v1", + "sequence": "102" } }, { - "name": "next-v2", "before": { - "mode": "classic-v2-only", "high_water_sequence": "101", + "mode": "classic-v2-only", + "pending_authorizations": 0, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f", "101112131415161718191a1b1c1d1e1f" ], + "tickets": 0, + "v1_authenticated_presence": false, + "v1_controls_active": false, "v1_listing_present": false, "v1_rendezvous_generation_present": false, - "v1_controls_active": false, - "pending_authorizations": 0, - "tickets": 0, "v2_usable": true }, - "request": { - "profile": "classic-v2", - "sequence": "102", - "nonce": "505152535455565758595a5b5c5d5e5f" - }, "expected": { "accepted": true, "minimum_next_sequence": "103", "state": { - "mode": "classic-v2-only", "high_water_sequence": "102", + "mode": "classic-v2-only", + "pending_authorizations": 0, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f", "101112131415161718191a1b1c1d1e1f", "505152535455565758595a5b5c5d5e5f" ], + "tickets": 0, + "v1_authenticated_presence": false, + "v1_controls_active": false, "v1_listing_present": false, "v1_rendezvous_generation_present": false, + "v2_usable": true + } + }, + "name": "next-v2", + "request": { + "nonce": "505152535455565758595a5b5c5d5e5f", + "profile": "classic-v2", + "sequence": "102" + } + }, + { + "before": { + "high_water_sequence": "18446744073709551614", + "mode": "classic-v2-only", + "pending_authorizations": 0, + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "tickets": 0, + "v1_authenticated_presence": false, + "v1_controls_active": false, + "v1_listing_present": false, + "v1_rendezvous_generation_present": false, + "v2_usable": true + }, + "expected": { + "accepted": true, + "minimum_next_sequence": "", + "state": { + "high_water_sequence": "18446744073709551615", + "mode": "classic-v2-only", + "pending_authorizations": 0, + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f", + "606162636465666768696a6b6c6d6e6f" + ], + "tickets": 0, + "v1_authenticated_presence": false, "v1_controls_active": false, + "v1_listing_present": false, + "v1_rendezvous_generation_present": false, + "v2_usable": true + } + }, + "name": "maximum-v2-sequence", + "request": { + "nonce": "606162636465666768696a6b6c6d6e6f", + "profile": "classic-v2", + "sequence": "18446744073709551615" + } + }, + { + "before": { + "high_water_sequence": "18446744073709551615", + "mode": "classic-v2-only", + "pending_authorizations": 0, + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f", + "606162636465666768696a6b6c6d6e6f" + ], + "tickets": 0, + "v1_authenticated_presence": false, + "v1_controls_active": false, + "v1_listing_present": false, + "v1_rendezvous_generation_present": false, + "v2_usable": true + }, + "expected": { + "accepted": false, + "error": "publish_sequence_exhausted", + "minimum_next_sequence": "", + "state": { + "high_water_sequence": "18446744073709551615", + "mode": "classic-v2-only", "pending_authorizations": 0, + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f", + "606162636465666768696a6b6c6d6e6f" + ], "tickets": 0, + "v1_authenticated_presence": false, + "v1_controls_active": false, + "v1_listing_present": false, + "v1_rendezvous_generation_present": false, "v2_usable": true } + }, + "name": "exhausted-lineage", + "request": { + "nonce": "707172737475767778797a7b7c7d7e7f", + "profile": "classic-v2", + "sequence": "18446744073709551615" } } ] - } + }, + "negative": [ + { + "name": "v1-schema", + "body": "{\"schema\":\"atrinik-classic-publish-v1\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "unsupported_schema" + }, + { + "name": "legacy-password-field", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"passwordRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "noncanonical_json" + }, + { + "name": "wrong-key-order", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"playersCount\":4,\"name\":\"Atrinik Classic\",\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "noncanonical_json" + }, + { + "name": "whitespace", + "body": "{ \"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "noncanonical_json" + }, + { + "name": "duplicate-field", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "noncanonical_json" + }, + { + "name": "unknown-field", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327,\"extra\":true}", + "error": "noncanonical_json" + }, + { + "name": "malformed-boolean", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":\"true\",\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "invalid_json" + }, + { + "name": "missing-policy", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "noncanonical_json" + }, + { + "name": "hostname-without-port", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\"}", + "error": "invalid_endpoint" + }, + { + "name": "port-without-hostname", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"port\":13327}", + "error": "invalid_endpoint" + }, + { + "name": "uppercase-server-id", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13E46C3B910727029646BA5DB7EE5930080F832AF4D9E29F70CF781250A23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "invalid_identity" + }, + { + "name": "noncanonical-certificate", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ=\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "invalid_certificate" + }, + { + "name": "players-overflow", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4294967296,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "invalid_players" + }, + { + "name": "numeric-hostname", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"192.0.2.1\",\"port\":13327}", + "error": "invalid_endpoint" + }, + { + "name": "uppercase-hostname", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"Play.example.org\",\"port\":13327}", + "error": "invalid_endpoint" + }, + { + "name": "control-text", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"bad\\nvalue\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "error": "invalid_text" + }, + { + "name": "trailing-input", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}{}", + "error": "noncanonical_json" + }, + { + "name": "endpoint-alias", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"endpoint\":{\"hostname\":\"play.example.org\",\"port\":13327}}", + "error": "noncanonical_json" + } + ], + "positive": [ + { + "name": "public-open-addressless", + "sequence": "101", + "nonce": "202122232425262728292a2b2c2d2e2f", + "created": 1800000000, + "expires": 1800000300, + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":3,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":false}", + "content_digest": "sha-256=:Gr86A+BT7xYperp9yC1Dpg2+AJf20bcontFR6hMJvxY=:", + "path": "/v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish", + "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000000;expires=1800000300;nonce=\"202122232425262728292a2b2c2d2e2f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:Gr86A+BT7xYperp9yC1Dpg2+AJf20bcontFR6hMJvxY=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 101\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000000;expires=1800000300;nonce=\"202122232425262728292a2b2c2d2e2f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "hn4Vp7Yk9oAcsT/ZJa3JjtPNPkojidIirbvpkugwnUncptdh7TMSkOBesO2BNR2UhWvirP798Nz1YLopF7tHOw==", + "signature_header": "atrinik=:hn4Vp7Yk9oAcsT/ZJa3JjtPNPkojidIirbvpkugwnUncptdh7TMSkOBesO2BNR2UhWvirP798Nz1YLopF7tHOw==:", + "expected_state": { + "authenticated_presence": true, + "public_listing": true, + "public_fields_retained": true, + "rendezvous": "unauthenticated", + "public_authorization_active": false, + "private_server_control_retained": false + } + }, + { + "name": "public-protected-endpoint", + "sequence": "102", + "nonce": "303132333435363738393a3b3c3d3e3f", + "created": 1800000600, + "expires": 1800000900, + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "content_digest": "sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:", + "path": "/v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish", + "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "signature_header": "atrinik=:1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==:", + "expected_state": { + "authenticated_presence": true, + "public_listing": true, + "public_fields_retained": true, + "rendezvous": "proof-required", + "public_authorization_active": true, + "private_server_control_retained": false + } + }, + { + "name": "private-open-endpoint", + "sequence": "103", + "nonce": "404142434445464748494a4b4c4d4e4f", + "created": 1800001200, + "expires": 1800001500, + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":5,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":false,\"accessCodeRequired\":false,\"hostname\":\"private.example.org\",\"port\":13328}", + "content_digest": "sha-256=:vDE4NdX7UjFFd8FvQAw6DJRGcWwkSlDVPQeoJ6WFF4c=:", + "path": "/v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish", + "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001200;expires=1800001500;nonce=\"404142434445464748494a4b4c4d4e4f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:vDE4NdX7UjFFd8FvQAw6DJRGcWwkSlDVPQeoJ6WFF4c=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 103\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001200;expires=1800001500;nonce=\"404142434445464748494a4b4c4d4e4f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "iqRbY+IrGsqLmv33cE2lxOd0cE3dRT70ER5I1W/R+0pqFzpITv4iYu5e0Zpu1GllDo1dRBx3btHG0qFiC/gaEA==", + "signature_header": "atrinik=:iqRbY+IrGsqLmv33cE2lxOd0cE3dRT70ER5I1W/R+0pqFzpITv4iYu5e0Zpu1GllDo1dRBx3btHG0qFiC/gaEA==:", + "expected_state": { + "authenticated_presence": true, + "public_listing": false, + "public_fields_retained": false, + "rendezvous": "not-found", + "public_authorization_active": false, + "private_server_control_retained": true + } + }, + { + "name": "private-protected-addressless", + "sequence": "104", + "nonce": "505152535455565758595a5b5c5d5e5f", + "created": 1800001800, + "expires": 1800002100, + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":6,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":false,\"accessCodeRequired\":true}", + "content_digest": "sha-256=:s56rYrOnUoGOi8+rE1WX6ztHKCHd7r9kmt8S5DX6uco=:", + "path": "/v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish", + "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001800;expires=1800002100;nonce=\"505152535455565758595a5b5c5d5e5f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:s56rYrOnUoGOi8+rE1WX6ztHKCHd7r9kmt8S5DX6uco=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 104\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001800;expires=1800002100;nonce=\"505152535455565758595a5b5c5d5e5f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "09ZOuTpd+zu1VRmV+Hs12jYGF4bVyxbCgLfNJR0PjsZyDA5bMni8P+4Ni/pYqMs261lxZe43J3iJSiseBxs4ng==", + "signature_header": "atrinik=:09ZOuTpd+zu1VRmV+Hs12jYGF4bVyxbCgLfNJR0PjsZyDA5bMni8P+4Ni/pYqMs261lxZe43J3iJSiseBxs4ng==:", + "expected_state": { + "authenticated_presence": true, + "public_listing": false, + "public_fields_retained": false, + "rendezvous": "not-found", + "public_authorization_active": false, + "private_server_control_retained": true + } + } + ], + "profile": "classic-v2", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "signature_negative": [ + { + "name": "body-content-digest", + "failure_class": "body_content_digest", + "positive": "public-protected-endpoint", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic mutated\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:wzSGCMNyq+fY+HvqHsUwSM+VBpBskOYmciOfTpyIGl0=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "error": "invalid_signature" + }, + { + "name": "authority", + "failure_class": "authority", + "positive": "public-protected-endpoint", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "signature_base": "\"@method\": POST\n\"@authority\": canary.publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "error": "invalid_signature" + }, + { + "name": "sequence", + "failure_class": "sequence", + "positive": "public-protected-endpoint", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 7\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "error": "invalid_signature" + }, + { + "name": "v2-signature-at-v1", + "failure_class": "route_tag_domain", + "positive": "public-protected-endpoint", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v1/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v1\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "error": "invalid_signature" + }, + { + "name": "v2-signature-at-game", + "failure_class": "route_tag_domain", + "positive": "public-protected-endpoint", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v1/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-game-publish-v1\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "error": "invalid_signature" + }, + { + "name": "wrong-v2-route", + "failure_class": "route", + "positive": "public-protected-endpoint", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/server/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "error": "invalid_signature" + }, + { + "name": "wrong-v2-tag", + "failure_class": "tag", + "positive": "public-protected-endpoint", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v3\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "error": "invalid_signature" + }, + { + "name": "signed-server-id-disagreement", + "failure_class": "signed_server_id", + "positive": "public-protected-endpoint", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 03e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "error": "invalid_signature" + }, + { + "name": "keyid-disagreement", + "failure_class": "keyid", + "positive": "public-protected-endpoint", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"03e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "error": "invalid_signature" + }, + { + "name": "certificate-fingerprint-mismatch", + "failure_class": "certificate_fingerprint", + "positive": "public-protected-endpoint", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "server_id": "03e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "error": "invalid_identity" + }, + { + "name": "malformed-certificate", + "failure_class": "certificate_der", + "positive": "public-protected-endpoint", + "certificate_der_base64": "bm90IGEgY2VydGlmaWNhdGU=", + "server_id": "47209c9b7af839de69e9a9cd625e9182c1ad63dae79ed88a2dd680fe34218620", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "error": "invalid_identity" + }, + { + "name": "non-p256-certificate", + "failure_class": "certificate_key_type", + "positive": "public-protected-endpoint", + "certificate_der_base64": "MIIB0zCCAVigAwIBAgIUYD79hLD6PhdnB+ntqGm9joG8RgYwCgYIKoZIzj0EAwIwIDEeMBwGA1UEAwwVQXRyaW5pay1QMzg0LW5lZ2F0aXZlMB4XDTI2MDgxNDAyNTI0N1oXDTM2MDgxMTAyNTI0N1owIDEeMBwGA1UEAwwVQXRyaW5pay1QMzg0LW5lZ2F0aXZlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEpkpDP1nwTmqDj1KvqCrULFkMSmv6Tar3SZRws9qrIY1fsGNHTSg36PYZL8RGYvJztpsnyBy+5t9Eff3cVEbWnwhzs78s0+REt4kuKlhYF/YYBK5P/bknu4SWZODHhTslo1MwUTAdBgNVHQ4EFgQUOVhnWI5Z9Ur56JzdIdTr4feKmXMwHwYDVR0jBBgwFoAUOVhnWI5Z9Ur56JzdIdTr4feKmXMwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNpADBmAjEAipJlthtoBzM6gVuIcrGoV72h5MdmefJIi4WMutcSyMHSBS6Xmx1YE3UpeUnEJ9K9AjEAulPPnUMBZ+EmCfTzVGBXqiT66fmoO56+UO5FOVvwar2WwBJuPXAL5BIMFACiXGv3", + "server_id": "42f63fc7bf6955c441bb8d9f00fec3f38cd3f0afd179d59754fd2618766edfee", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "error": "invalid_identity" + }, + { + "name": "malformed-p1363-length", + "failure_class": "signature_length", + "positive": "public-protected-endpoint", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKR", + "error": "invalid_identity" + }, + { + "name": "zero-p1363-coordinate", + "failure_class": "signature_coordinate", + "positive": "public-protected-endpoint", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "error": "invalid_signature" + }, + { + "name": "out-of-range-p1363-coordinate", + "failure_class": "signature_coordinate", + "positive": "public-protected-endpoint", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "/////////////////////////////////////////////////////////////////////////////////////w==", + "error": "invalid_signature" + } + ] } diff --git a/metaserver/classic_publisher_test.go b/metaserver/classic_publisher_test.go index 6baa252..7dca44b 100644 --- a/metaserver/classic_publisher_test.go +++ b/metaserver/classic_publisher_test.go @@ -9,6 +9,7 @@ import ( "encoding/hex" "encoding/json" "errors" + "math" "os" "reflect" "strconv" @@ -50,11 +51,15 @@ type classicV2NegativeFixture struct { } type classicV2SignatureNegativeFixture struct { - Name string `json:"name"` - Positive string `json:"positive"` - SignatureBase string `json:"signature_base"` - SignatureBase64 string `json:"signature_base64"` - Error string `json:"error"` + Name string `json:"name"` + FailureClass string `json:"failure_class"` + Positive string `json:"positive"` + CertificateDERBase64 string `json:"certificate_der_base64"` + ServerID string `json:"server_id"` + Body string `json:"body"` + SignatureBase string `json:"signature_base"` + SignatureBase64 string `json:"signature_base64"` + Error string `json:"error"` } type classicV2CrossProfileReplayFixture struct { @@ -75,6 +80,7 @@ type classicV2MigrationState struct { Mode string `json:"mode"` HighWaterSequence string `json:"high_water_sequence"` RetainedNonces []string `json:"retained_nonces"` + V1AuthenticatedPresence bool `json:"v1_authenticated_presence"` V1ListingPresent bool `json:"v1_listing_present"` V1RendezvousGenerationPresent bool `json:"v1_rendezvous_generation_present"` V1ControlsActive bool `json:"v1_controls_active"` @@ -103,6 +109,37 @@ type classicV2MigrationCase struct { Expected classicV2MigrationExpected `json:"expected"` } +type classicV2GlobalRetirementState struct { + Mode string `json:"mode"` + InFlightV1Commits int `json:"in_flight_v1_commits"` + V1AuthenticatedPresence int `json:"v1_authenticated_presences"` + V1Listings int `json:"v1_listings"` + V1RendezvousGenerations int `json:"v1_rendezvous_generations"` + V1Controls int `json:"v1_controls"` + PendingAuthorizations int `json:"pending_authorizations"` + Tickets int `json:"tickets"` + V2StateUnchanged bool `json:"v2_state_unchanged"` + RollbackAllowed bool `json:"rollback_allowed"` +} + +type classicV2GlobalRetirementFixture struct { + Before classicV2GlobalRetirementState `json:"before"` + Activation struct { + Kind string `json:"kind"` + Prerequisite string `json:"prerequisite"` + } `json:"activation"` + ExpectedAfter classicV2GlobalRetirementState `json:"expected_after"` + RejectedPublish struct { + Profile string `json:"profile"` + HTTPStatus int `json:"http_status"` + CacheControl string `json:"cache_control"` + Body string `json:"body"` + Inspection string `json:"inspection"` + SequenceOrNonceConsumed bool `json:"sequence_or_nonce_consumed"` + MinimumNextSequence string `json:"minimum_next_sequence"` + } `json:"rejected_publish"` +} + type classicV2Fixture struct { FixtureVersion int `json:"fixture_version"` Profile string `json:"profile"` @@ -128,35 +165,65 @@ type classicV2Fixture struct { Migration struct { Cases []classicV2MigrationCase `json:"cases"` } `json:"migration"` + GlobalV1Retirement classicV2GlobalRetirementFixture `json:"global_v1_retirement"` } func TestClassicV2PublisherSignatureNegativeFixtures(t *testing.T) { fixture := loadClassicV2Fixture(t) - if len(fixture.SignatureNegative) != 4 { - t.Fatal("signature-negative fixture does not cover route and tag domains") + if len(fixture.SignatureNegative) != 15 { + t.Fatal("signature-negative fixture does not cover every required failure class") } - certificate := decodeBase64(t, fixture.CertificateDERBase64) - positiveSignatures := make(map[string]string, len(fixture.Positive)) + positives := make(map[string]classicV2PositiveFixture, len(fixture.Positive)) for _, positive := range fixture.Positive { - positiveSignatures[positive.Name] = positive.SignatureBase64 + positives[positive.Name] = positive } + seen := make(map[string]bool) for _, test := range fixture.SignatureNegative { t.Run(test.Name, func(t *testing.T) { - if test.Error != "invalid_signature" || test.Positive == "" || test.SignatureBase == "" || - positiveSignatures[test.Positive] != test.SignatureBase64 { + positive, ok := positives[test.Positive] + if !ok || test.FailureClass == "" || test.CertificateDERBase64 == "" || test.ServerID == "" || + test.Body == "" || test.SignatureBase == "" || test.SignatureBase64 == "" { t.Fatal("signature-negative fixture metadata is incomplete") } + if test.FailureClass == "body_content_digest" { + parameters := classicV2FixtureParameters(t, fixture, positive, metaserver.ClassicV2Profile) + components, err := metaserver.Build(parameters, []byte(test.Body)) + if err != nil || components.SignatureBase != test.SignatureBase { + t.Fatalf("mutated body does not construct the declared signature base: %v", err) + } + } + certificate := decodeBase64(t, test.CertificateDERBase64) signature := decodeBase64(t, test.SignatureBase64) - if err := metaserver.VerifyCertificateSignature( + err := metaserver.VerifyCertificateSignature( certificate, - fixture.ServerID, + test.ServerID, test.SignatureBase, signature, - ); !errors.Is(err, metaserver.ErrInvalidSignature) { - t.Fatalf("negative signature verified: %v", err) + ) + switch test.Error { + case "invalid_identity": + if !errors.Is(err, metaserver.ErrInvalidIdentity) { + t.Fatalf("identity failure returned %v", err) + } + case "invalid_signature": + if !errors.Is(err, metaserver.ErrInvalidSignature) { + t.Fatalf("signature failure returned %v", err) + } + default: + t.Fatalf("unknown expected error %q", test.Error) } + seen[test.FailureClass] = true }) } + for _, required := range []string{ + "body_content_digest", "authority", "sequence", "route_tag_domain", "route", "tag", + "signed_server_id", "keyid", "certificate_fingerprint", "certificate_der", + "certificate_key_type", "signature_length", "signature_coordinate", + } { + if !seen[required] { + t.Fatalf("signature-negative fixture is missing %q", required) + } + } } func TestClassicV2PublisherGoldenFixtures(t *testing.T) { @@ -339,6 +406,7 @@ func TestClassicV2PublisherNegativeFixtures(t *testing.T) { if len(fixture.Negative) < 12 { t.Fatal("negative fixture does not cover the canonical contract") } + seen := make(map[string]bool) for _, test := range fixture.Negative { t.Run(test.Name, func(t *testing.T) { request, err := metaserver.ParseClassicV2PublishJSON([]byte(test.Body)) @@ -349,14 +417,18 @@ func TestClassicV2PublisherNegativeFixtures(t *testing.T) { if !ok || code != test.Error { t.Fatalf("rejected body returned (%q, %v), want %q", code, err, test.Error) } + seen[test.Name] = true }) } + if !seen["endpoint-alias"] { + t.Fatal("negative fixture does not cover an endpoint object alias") + } } func TestClassicV2MigrationFixture(t *testing.T) { fixture := loadClassicV2Fixture(t) - if len(fixture.Migration.Cases) != 6 { - t.Fatalf("migration case count %d, want 6", len(fixture.Migration.Cases)) + if len(fixture.Migration.Cases) != 8 { + t.Fatalf("migration case count %d, want 8", len(fixture.Migration.Cases)) } for _, test := range fixture.Migration.Cases { t.Run(test.Name, func(t *testing.T) { @@ -395,14 +467,18 @@ func applyClassicV2MigrationFixture(t *testing.T, before classicV2MigrationState } result := classicV2MigrationExpected{ - MinimumNextSequence: strconv.FormatUint(highWater+1, 10), - State: before, + State: before, } result.State.RetainedNonces = append([]string(nil), before.RetainedNonces...) if before.Mode == "classic-v2-only" && request.Profile == "classic-v1" { result.Error = "profile_retired" return result } + if highWater == math.MaxUint64 { + result.Error = "publish_sequence_exhausted" + return result + } + result.MinimumNextSequence = strconv.FormatUint(highWater+1, 10) for _, nonce := range before.RetainedNonces { if nonce == request.Nonce { result.Error = "publish_replay" @@ -415,11 +491,16 @@ func applyClassicV2MigrationFixture(t *testing.T, before classicV2MigrationState } result.Accepted = true - result.MinimumNextSequence = strconv.FormatUint(sequence+1, 10) + if sequence != math.MaxUint64 { + result.MinimumNextSequence = strconv.FormatUint(sequence+1, 10) + } else { + result.MinimumNextSequence = "" + } result.State.HighWaterSequence = request.Sequence result.State.RetainedNonces = append(result.State.RetainedNonces, request.Nonce) if request.Profile == "classic-v2" { result.State.Mode = "classic-v2-only" + result.State.V1AuthenticatedPresence = false result.State.V1ListingPresent = false result.State.V1RendezvousGenerationPresent = false result.State.V1ControlsActive = false @@ -430,6 +511,34 @@ func applyClassicV2MigrationFixture(t *testing.T, before classicV2MigrationState return result } +func TestClassicV2GlobalRetirementFixture(t *testing.T) { + gate := loadClassicV2Fixture(t).GlobalV1Retirement + if gate.Before.Mode != "classic-v1-accepting" || gate.Before.InFlightV1Commits != 0 || + gate.Before.V1AuthenticatedPresence == 0 || gate.Before.V1Listings == 0 || + gate.Activation.Kind != "authorized-operator-transaction" || + gate.Activation.Prerequisite != "human-accepted-v5-canaries-and-one-way-alias-cutover" { + t.Fatal("global retirement fixture lacks an explicit drained starting gate and human prerequisite") + } + after := gate.Before + after.Mode = "classic-v1-retired" + after.V1AuthenticatedPresence = 0 + after.V1Listings = 0 + after.V1RendezvousGenerations = 0 + after.V1Controls = 0 + after.PendingAuthorizations = 0 + after.Tickets = 0 + after.RollbackAllowed = false + if after != gate.ExpectedAfter || !gate.ExpectedAfter.V2StateUnchanged { + t.Fatalf("global retirement transition %#v, want %#v", gate.ExpectedAfter, after) + } + rejection := gate.RejectedPublish + if rejection.Profile != "classic-v1" || rejection.HTTPStatus != 410 || + rejection.CacheControl != "no-store" || rejection.Body != `{"error":{"code":"profile_retired"}}` || + rejection.Inspection != "none" || rejection.SequenceOrNonceConsumed || rejection.MinimumNextSequence != "" { + t.Fatal("global retirement rejection is not fixed, pre-inspection, and non-mutating") + } +} + func TestClassicV2PublisherValidationClasses(t *testing.T) { fixture := loadClassicV2Fixture(t) base, err := metaserver.ParseClassicV2PublishJSON([]byte(fixture.Positive[1].Body)) diff --git a/spec/metaserver-publisher.md b/spec/metaserver-publisher.md index 573f3a0..4a8974e 100644 --- a/spec/metaserver-publisher.md +++ b/spec/metaserver-publisher.md @@ -241,7 +241,11 @@ at least the advertised minimum. A minimum of zero is invalid. If either the receiver's stored sequence or the publisher's local high-water mark is the uint64 maximum, publication for that identity is exhausted and requires explicit operator recovery; it never wraps and no larger minimum can be -represented. +represented. A request rejected because the receiver is already exhausted +returns HTTP 409 with stable code `publish_sequence_exhausted` and no +`minimumNextSequence`. An accepted request whose sequence is the uint64 +maximum succeeds normally but advertises no next sequence; every later request +is exhausted. Accepted nonces are retained for at least 24 hours, bounded by the authenticated 48-publishes-per-UTC-day identity limit and pruned after expiry. Sequence @@ -270,6 +274,14 @@ The success body for all profiles is exactly `{"status":"ok","rendezvousToken":"{64-lower-hex}"}`. A replay conflict is exactly `{"error":{"code":"publish_replay","minimumNextSequence":"{uint64}"}}`. +An exhausted lineage is exactly +`{"error":{"code":"publish_sequence_exhausted"}}`. Before the global gate, +an authenticated v1 request for an identity already upgraded to v2 returns +HTTP 410 and exactly `{"error":{"code":"profile_retired"}}`; it consumes no +sequence or nonce and carries no `minimumNextSequence`. After the global gate, +the v1 route returns that same fixed HTTP 410 response before inspecting the +request body, signature, identity, sequence, or nonce. All 410 responses also +use `Cache-Control: no-store`. Rate-limit errors use the deployment's bounded error envelope, return HTTP 429, and carry matching delta-seconds in both `Retry-After` and `error.retry_after_seconds`; publishers treat that response as a consumed @@ -285,11 +297,22 @@ and directory artifacts. Classic v2 is a new, fail-closed profile. A cutover Classic server publishes v2 only and never negotiates or falls back to v1. Temporary v1 availability serves only identities which have not upgraded. At the explicit coordinated -global retirement gate, receivers reject every new Classic v1 publication, -expire or tombstone remaining v1 presence/listings/rooms, converge the v2-only -directory, and then contract retired state. This gate is not an indefinite -dual-producer policy, and recovery after production v2 exposure is -roll-forward. +global retirement gate, an authorized operator performs one serialized, +durable transition from receiver mode `classic-v1-accepting` to +`classic-v1-retired`. The external activation prerequisite is explicit human +acceptance of the program's v5 production canaries and one-way alias cutover; +elapsed time, traffic, a deploy, or the arrival of a v2 publication never +activates it implicitly. The transaction first excludes and drains in-flight +v1 state commits, then durably marks the retired mode and atomically expires or +tombstones every remaining v1 authenticated presence, listing, rendezvous +generation, control, pending authorization, and ticket before publishing the +converged v2-only directory generation. V2 identities, replay state, and +availability are unchanged. All replicas, backups, restores, and replacement +deployments inherit the retired mode. The transition is one-way: rollback may +restore implementation code but must not reopen v1; recovery after production +v2 exposure is roll-forward. Retired per-identity replay records may be +contracted only after the mode and v1 tombstones are durable. The normative +gate transition and fixed rejection are encoded in the shared fixture. Game Protocol 1 never uses either Classic form, key, route, schema, signature tag, migration marker, or replay lineage. Its `passwordRequired` semantics and From aba06bf16ec2bdee4e72f6c1e60b851709c25910 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Fri, 14 Aug 2026 03:04:44 +0000 Subject: [PATCH 4/6] fix(metaserver): bind migration fixtures to identities --- fixtures/metaserver-classic-publisher-v2.json | 700 +++++++++++++----- metaserver/classic_publisher_test.go | 244 +++++- spec/metaserver-publisher.md | 14 + 3 files changed, 755 insertions(+), 203 deletions(-) diff --git a/fixtures/metaserver-classic-publisher-v2.json b/fixtures/metaserver-classic-publisher-v2.json index 78be0e9..84b5a62 100644 --- a/fixtures/metaserver-classic-publisher-v2.json +++ b/fixtures/metaserver-classic-publisher-v2.json @@ -4,29 +4,29 @@ "content_type": "application/json", "cross_profile_replay": [ { + "body": "{\"schema\":\"atrinik-classic-publish-v1\",\"serverId\":\"51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b\",\"certificate\":\"MIIBRzCB76ADAgECAgEBMAoGCCqGSM49BAMCMCQxIjAgBgNVBAMTGUF0cmluaWsgcHVibGlzaGVyIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAkMSIwIAYDVQQDExlBdHJpbmlrIHB1Ymxpc2hlciBmaXh0dXJlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPQd9J89i9RHBKxJbWY3LVTOACf0pjFZgJTnCMy6BlQ27hfl0xwZf5LkuREpxVbe0xh6ydj5V00OY5Sh5myB4pqMSMBAwDgYDVR0PAQH/BAQDAgeAMAoGCCqGSM49BAMCA0cAMEQCIAbaCO4YENW0H67c1Gj292fvPwTwvwsQ8F5ObWMOv0wyAiArIt0mJkn917HvWQw8HrBVOCyXfGfocde6yjbjwSUcEA==\",\"name\":\"Atrinik Classic\",\"playersCount\":3,\"version\":\"5.7.0\",\"textComment\":\"Fixture\",\"public\":true,\"passwordRequired\":true}", + "certificate_der_base64": "MIIBRzCB76ADAgECAgEBMAoGCCqGSM49BAMCMCQxIjAgBgNVBAMTGUF0cmluaWsgcHVibGlzaGVyIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAkMSIwIAYDVQQDExlBdHJpbmlrIHB1Ymxpc2hlciBmaXh0dXJlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPQd9J89i9RHBKxJbWY3LVTOACf0pjFZgJTnCMy6BlQ27hfl0xwZf5LkuREpxVbe0xh6ydj5V00OY5Sh5myB4pqMSMBAwDgYDVR0PAQH/BAQDAgeAMAoGCCqGSM49BAMCA0cAMEQCIAbaCO4YENW0H67c1Gj292fvPwTwvwsQ8F5ObWMOv0wyAiArIt0mJkn917HvWQw8HrBVOCyXfGfocde6yjbjwSUcEA==", "name": "v1-at-v2", - "source_profile": "classic-v1", - "target_profile": "classic-v2", "server_id": "51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b", - "certificate_der_base64": "MIIBRzCB76ADAgECAgEBMAoGCCqGSM49BAMCMCQxIjAgBgNVBAMTGUF0cmluaWsgcHVibGlzaGVyIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAkMSIwIAYDVQQDExlBdHJpbmlrIHB1Ymxpc2hlciBmaXh0dXJlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPQd9J89i9RHBKxJbWY3LVTOACf0pjFZgJTnCMy6BlQ27hfl0xwZf5LkuREpxVbe0xh6ydj5V00OY5Sh5myB4pqMSMBAwDgYDVR0PAQH/BAQDAgeAMAoGCCqGSM49BAMCA0cAMEQCIAbaCO4YENW0H67c1Gj292fvPwTwvwsQ8F5ObWMOv0wyAiArIt0mJkn917HvWQw8HrBVOCyXfGfocde6yjbjwSUcEA==", - "body": "{\"schema\":\"atrinik-classic-publish-v1\",\"serverId\":\"51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b\",\"certificate\":\"MIIBRzCB76ADAgECAgEBMAoGCCqGSM49BAMCMCQxIjAgBgNVBAMTGUF0cmluaWsgcHVibGlzaGVyIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAkMSIwIAYDVQQDExlBdHJpbmlrIHB1Ymxpc2hlciBmaXh0dXJlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPQd9J89i9RHBKxJbWY3LVTOACf0pjFZgJTnCMy6BlQ27hfl0xwZf5LkuREpxVbe0xh6ydj5V00OY5Sh5myB4pqMSMBAwDgYDVR0PAQH/BAQDAgeAMAoGCCqGSM49BAMCA0cAMEQCIAbaCO4YENW0H67c1Gj292fvPwTwvwsQ8F5ObWMOv0wyAiArIt0mJkn917HvWQw8HrBVOCyXfGfocde6yjbjwSUcEA==\",\"name\":\"Atrinik Classic\",\"playersCount\":3,\"version\":\"5.7.0\",\"textComment\":\"Fixture\",\"public\":true,\"passwordRequired\":true}", - "source_signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v1/classic/servers/51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b/publish\n\"content-digest\": sha-256=:7r1sfMoSYvvgYGEEOEHGcIKysWU8LtvUcNxXBdd/hyw=:\n\"content-type\": application/json\n\"atrinik-server-id\": 51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b\n\"atrinik-publish-sequence\": 18446744073709551610\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000000;expires=1800000300;nonce=\"000102030405060708090a0b0c0d0e0f\";alg=\"ecdsa-p256-sha256\";keyid=\"51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b\";tag=\"atrinik-classic-publish-v1\"", - "target_signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b/publish\n\"content-digest\": sha-256=:7r1sfMoSYvvgYGEEOEHGcIKysWU8LtvUcNxXBdd/hyw=:\n\"content-type\": application/json\n\"atrinik-server-id\": 51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b\n\"atrinik-publish-sequence\": 18446744073709551610\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000000;expires=1800000300;nonce=\"000102030405060708090a0b0c0d0e0f\";alg=\"ecdsa-p256-sha256\";keyid=\"51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b\";tag=\"atrinik-classic-publish-v2\"", "signature_base64": "ec3TLCXjfy0FCqNAL/cdtrWSRj2ktJ2v81L7yuzgpw270zFhROANO0rUQ5y0hU5NSOfVai2chQlbNY9zHEffIw==", + "source_profile": "classic-v1", + "source_signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v1/classic/servers/51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b/publish\n\"content-digest\": sha-256=:7r1sfMoSYvvgYGEEOEHGcIKysWU8LtvUcNxXBdd/hyw=:\n\"content-type\": application/json\n\"atrinik-server-id\": 51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b\n\"atrinik-publish-sequence\": 18446744073709551610\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000000;expires=1800000300;nonce=\"000102030405060708090a0b0c0d0e0f\";alg=\"ecdsa-p256-sha256\";keyid=\"51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b\";tag=\"atrinik-classic-publish-v1\"", "target_body_error": "unsupported_schema", + "target_profile": "classic-v2", + "target_signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b/publish\n\"content-digest\": sha-256=:7r1sfMoSYvvgYGEEOEHGcIKysWU8LtvUcNxXBdd/hyw=:\n\"content-type\": application/json\n\"atrinik-server-id\": 51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b\n\"atrinik-publish-sequence\": 18446744073709551610\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000000;expires=1800000300;nonce=\"000102030405060708090a0b0c0d0e0f\";alg=\"ecdsa-p256-sha256\";keyid=\"51f341b3c529d7186d2981afd2d8519c6c15eaced3f0291acdfb408f5cba746b\";tag=\"atrinik-classic-publish-v2\"", "target_signature_error": "invalid_signature" }, { + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", "name": "v2-at-v1", - "source_profile": "classic-v2", - "target_profile": "classic-v1", "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "source_signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "target_signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v1/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v1\"", "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", + "source_profile": "classic-v2", + "source_signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", "target_body_error": "unsupported_schema", + "target_profile": "classic-v1", + "target_signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v1/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v1\"", "target_signature_error": "invalid_signature" } ], @@ -47,6 +47,12 @@ "v1_rendezvous_generations": 1, "v2_state_unchanged": true }, + "concurrent_activation": { + "behavior": "exclude-new-v1-and-drain-before-retirement-commit", + "expected_recovery": "exact-after", + "racing_v1_result": "profile_retired-after-commit", + "starting_in_flight_v1_commits": 1 + }, "expected_after": { "in_flight_v1_commits": 0, "mode": "classic-v1-retired", @@ -59,6 +65,20 @@ "v1_rendezvous_generations": 0, "v2_state_unchanged": true }, + "failure_transitions": [ + { + "durable_retired_mode": false, + "expected_recovery": "exact-before", + "failure_phase": "after-drain-before-durable-retired-mode", + "name": "crash-before-retired-mode" + }, + { + "durable_retired_mode": true, + "expected_recovery": "exact-after", + "failure_phase": "after-durable-retired-mode-before-tombstones-complete", + "name": "crash-after-retired-mode" + } + ], "rejected_publish": { "body": "{\"error\":{\"code\":\"profile_retired\"}}", "cache_control": "no-store", @@ -72,12 +92,12 @@ "maximum_bounds": { "body_bytes": 4096, "certificate_der_bytes": 2048, + "hostname_bytes": 253, "name_utf8_bytes": 80, "players_count": 4294967295, - "version_utf8_bytes": 32, + "port": 65535, "text_comment_utf8_bytes": 256, - "hostname_bytes": 253, - "port": 65535 + "version_utf8_bytes": 32 }, "method": "POST", "migration": { @@ -85,12 +105,21 @@ { "before": { "high_water_sequence": "100", + "last_accepted_envelope": "v1-100", "mode": "classic-v1", "pending_authorizations": 2, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, + "unaffected_identity": { + "high_water_sequence": "102", + "mode": "classic-v2-only", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "v1_listing": false, + "v1_presence": false + }, "v1_authenticated_presence": true, "v1_controls_active": true, "v1_listing_present": true, @@ -103,12 +132,21 @@ "minimum_next_sequence": "101", "state": { "high_water_sequence": "100", + "last_accepted_envelope": "v1-100", "mode": "classic-v1", "pending_authorizations": 2, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, + "unaffected_identity": { + "high_water_sequence": "102", + "mode": "classic-v2-only", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "v1_listing": false, + "v1_presence": false + }, "v1_authenticated_presence": true, "v1_controls_active": true, "v1_listing_present": true, @@ -120,18 +158,29 @@ "request": { "nonce": "202122232425262728292a2b2c2d2e2f", "profile": "classic-v2", - "sequence": "100" + "sequence": "100", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signed_envelope": "v2-100-equal" } }, { "before": { "high_water_sequence": "100", + "last_accepted_envelope": "v1-100", "mode": "classic-v1", "pending_authorizations": 2, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, + "unaffected_identity": { + "high_water_sequence": "102", + "mode": "classic-v2-only", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "v1_listing": false, + "v1_presence": false + }, "v1_authenticated_presence": true, "v1_controls_active": true, "v1_listing_present": true, @@ -144,12 +193,21 @@ "minimum_next_sequence": "101", "state": { "high_water_sequence": "100", + "last_accepted_envelope": "v1-100", "mode": "classic-v1", "pending_authorizations": 2, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, + "unaffected_identity": { + "high_water_sequence": "102", + "mode": "classic-v2-only", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "v1_listing": false, + "v1_presence": false + }, "v1_authenticated_presence": true, "v1_controls_active": true, "v1_listing_present": true, @@ -161,18 +219,29 @@ "request": { "nonce": "303132333435363738393a3b3c3d3e3f", "profile": "classic-v2", - "sequence": "99" + "sequence": "99", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signed_envelope": "v2-99-stale" } }, { "before": { "high_water_sequence": "100", + "last_accepted_envelope": "v1-100", "mode": "classic-v1", "pending_authorizations": 2, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, + "unaffected_identity": { + "high_water_sequence": "102", + "mode": "classic-v2-only", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "v1_listing": false, + "v1_presence": false + }, "v1_authenticated_presence": true, "v1_controls_active": true, "v1_listing_present": true, @@ -185,12 +254,21 @@ "minimum_next_sequence": "101", "state": { "high_water_sequence": "100", + "last_accepted_envelope": "v1-100", "mode": "classic-v1", "pending_authorizations": 2, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, + "unaffected_identity": { + "high_water_sequence": "102", + "mode": "classic-v2-only", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "v1_listing": false, + "v1_presence": false + }, "v1_authenticated_presence": true, "v1_controls_active": true, "v1_listing_present": true, @@ -202,18 +280,29 @@ "request": { "nonce": "000102030405060708090a0b0c0d0e0f", "profile": "classic-v2", - "sequence": "101" + "sequence": "101", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signed_envelope": "v2-101-retained" } }, { "before": { "high_water_sequence": "100", + "last_accepted_envelope": "v1-100", "mode": "classic-v1", "pending_authorizations": 2, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, + "unaffected_identity": { + "high_water_sequence": "102", + "mode": "classic-v2-only", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "v1_listing": false, + "v1_presence": false + }, "v1_authenticated_presence": true, "v1_controls_active": true, "v1_listing_present": true, @@ -225,13 +314,22 @@ "minimum_next_sequence": "102", "state": { "high_water_sequence": "101", + "last_accepted_envelope": "v2-101-upgrade", "mode": "classic-v2-only", "pending_authorizations": 0, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f", "101112131415161718191a1b1c1d1e1f" ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, + "unaffected_identity": { + "high_water_sequence": "102", + "mode": "classic-v2-only", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "v1_listing": false, + "v1_presence": false + }, "v1_authenticated_presence": false, "v1_controls_active": false, "v1_listing_present": false, @@ -243,19 +341,30 @@ "request": { "nonce": "101112131415161718191a1b1c1d1e1f", "profile": "classic-v2", - "sequence": "101" + "sequence": "101", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signed_envelope": "v2-101-upgrade" } }, { "before": { "high_water_sequence": "101", + "last_accepted_envelope": "v2-101-upgrade", "mode": "classic-v2-only", "pending_authorizations": 0, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f", "101112131415161718191a1b1c1d1e1f" ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, + "unaffected_identity": { + "high_water_sequence": "102", + "mode": "classic-v2-only", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "v1_listing": false, + "v1_presence": false + }, "v1_authenticated_presence": false, "v1_controls_active": false, "v1_listing_present": false, @@ -268,13 +377,22 @@ "minimum_next_sequence": "", "state": { "high_water_sequence": "101", + "last_accepted_envelope": "v2-101-upgrade", "mode": "classic-v2-only", "pending_authorizations": 0, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f", "101112131415161718191a1b1c1d1e1f" ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, + "unaffected_identity": { + "high_water_sequence": "102", + "mode": "classic-v2-only", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "v1_listing": false, + "v1_presence": false + }, "v1_authenticated_presence": false, "v1_controls_active": false, "v1_listing_present": false, @@ -286,19 +404,30 @@ "request": { "nonce": "404142434445464748494a4b4c4d4e4f", "profile": "classic-v1", - "sequence": "102" + "sequence": "102", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signed_envelope": "v1-102-post-upgrade" } }, { "before": { "high_water_sequence": "101", + "last_accepted_envelope": "v2-101-upgrade", "mode": "classic-v2-only", "pending_authorizations": 0, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f", "101112131415161718191a1b1c1d1e1f" ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, + "unaffected_identity": { + "high_water_sequence": "102", + "mode": "classic-v2-only", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "v1_listing": false, + "v1_presence": false + }, "v1_authenticated_presence": false, "v1_controls_active": false, "v1_listing_present": false, @@ -310,6 +439,7 @@ "minimum_next_sequence": "103", "state": { "high_water_sequence": "102", + "last_accepted_envelope": "v2-102-next", "mode": "classic-v2-only", "pending_authorizations": 0, "retained_nonces": [ @@ -317,7 +447,15 @@ "101112131415161718191a1b1c1d1e1f", "505152535455565758595a5b5c5d5e5f" ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, + "unaffected_identity": { + "high_water_sequence": "102", + "mode": "classic-v2-only", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "v1_listing": false, + "v1_presence": false + }, "v1_authenticated_presence": false, "v1_controls_active": false, "v1_listing_present": false, @@ -329,18 +467,30 @@ "request": { "nonce": "505152535455565758595a5b5c5d5e5f", "profile": "classic-v2", - "sequence": "102" + "sequence": "102", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signed_envelope": "v2-102-next" } }, { "before": { "high_water_sequence": "18446744073709551614", + "last_accepted_envelope": "v2-max-minus-one", "mode": "classic-v2-only", "pending_authorizations": 0, "retained_nonces": [ - "000102030405060708090a0b0c0d0e0f" + "000102030405060708090a0b0c0d0e0f", + "808182838485868788898a8b8c8d8e8f" ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, + "unaffected_identity": { + "high_water_sequence": "102", + "mode": "classic-v2-only", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "v1_listing": false, + "v1_presence": false + }, "v1_authenticated_presence": false, "v1_controls_active": false, "v1_listing_present": false, @@ -352,13 +502,23 @@ "minimum_next_sequence": "", "state": { "high_water_sequence": "18446744073709551615", + "last_accepted_envelope": "v2-max", "mode": "classic-v2-only", "pending_authorizations": 0, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f", + "808182838485868788898a8b8c8d8e8f", "606162636465666768696a6b6c6d6e6f" ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, + "unaffected_identity": { + "high_water_sequence": "102", + "mode": "classic-v2-only", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "v1_listing": false, + "v1_presence": false + }, "v1_authenticated_presence": false, "v1_controls_active": false, "v1_listing_present": false, @@ -370,19 +530,31 @@ "request": { "nonce": "606162636465666768696a6b6c6d6e6f", "profile": "classic-v2", - "sequence": "18446744073709551615" + "sequence": "18446744073709551615", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signed_envelope": "v2-max" } }, { "before": { "high_water_sequence": "18446744073709551615", + "last_accepted_envelope": "v2-max", "mode": "classic-v2-only", "pending_authorizations": 0, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f", + "808182838485868788898a8b8c8d8e8f", "606162636465666768696a6b6c6d6e6f" ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, + "unaffected_identity": { + "high_water_sequence": "102", + "mode": "classic-v2-only", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "v1_listing": false, + "v1_presence": false + }, "v1_authenticated_presence": false, "v1_controls_active": false, "v1_listing_present": false, @@ -395,13 +567,23 @@ "minimum_next_sequence": "", "state": { "high_water_sequence": "18446744073709551615", + "last_accepted_envelope": "v2-max", "mode": "classic-v2-only", "pending_authorizations": 0, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f", + "808182838485868788898a8b8c8d8e8f", "606162636465666768696a6b6c6d6e6f" ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, + "unaffected_identity": { + "high_water_sequence": "102", + "mode": "classic-v2-only", + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "v1_listing": false, + "v1_presence": false + }, "v1_authenticated_presence": false, "v1_controls_active": false, "v1_listing_present": false, @@ -413,360 +595,500 @@ "request": { "nonce": "707172737475767778797a7b7c7d7e7f", "profile": "classic-v2", - "sequence": "18446744073709551615" + "sequence": "18446744073709551615", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signed_envelope": "v2-max-exhausted" } } + ], + "failure_transitions": [ + { + "case": "higher-v2-upgrade", + "durable_upgrade_marker": false, + "expected_recovery": "exact-before", + "failure_phase": "before-durable-v2-only-marker", + "name": "crash-before-upgrade-marker" + }, + { + "case": "higher-v2-upgrade", + "durable_upgrade_marker": true, + "expected_recovery": "exact-after", + "failure_phase": "after-durable-v2-only-marker-before-retirement-complete", + "name": "crash-after-upgrade-marker" + } + ], + "signed_envelopes": [ + { + "body": "{\"schema\":\"atrinik-classic-publish-v1\",\"serverId\":\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\",\"certificate\":\"MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==\",\"name\":\"Atrinik Migration\",\"playersCount\":2,\"version\":\"5.7.0\",\"textComment\":\"Migration fixture\",\"public\":true,\"passwordRequired\":true}", + "certificate_der_base64": "MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==", + "created": 1800010000, + "name": "v1-100", + "nonce": "000102030405060708090a0b0c0d0e0f", + "profile": "classic-v1", + "sequence": "100", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v1/classic/servers/c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694/publish\n\"content-digest\": sha-256=:a9IqOwLcZpB4+s5JSwMVl0ShdqPDdsrQbKCXGpPOlDk=:\n\"content-type\": application/json\n\"atrinik-server-id\": c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\n\"atrinik-publish-sequence\": 100\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800010000;expires=1800010300;nonce=\"000102030405060708090a0b0c0d0e0f\";alg=\"ecdsa-p256-sha256\";keyid=\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\";tag=\"atrinik-classic-publish-v1\"", + "signature_base64": "0EFzV/tMHx6QgYiUsVCRglIbBQBNNhhGvyEtE3sAY+qGMq3/JS5vmjWs+nGZgX4W3ShU/kb2XemPTXtGXN3bOA==" + }, + { + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\",\"certificate\":\"MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==\",\"name\":\"Atrinik Migration\",\"playersCount\":2,\"version\":\"6.0.0\",\"textComment\":\"Migration fixture\",\"public\":true,\"accessCodeRequired\":true}", + "certificate_der_base64": "MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==", + "created": 1800010600, + "name": "v2-100-equal", + "nonce": "202122232425262728292a2b2c2d2e2f", + "profile": "classic-v2", + "sequence": "100", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694/publish\n\"content-digest\": sha-256=:E1zmfLZvarnPmCAr3tt+Ely7EoWB35guMRvEYsn9QPs=:\n\"content-type\": application/json\n\"atrinik-server-id\": c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\n\"atrinik-publish-sequence\": 100\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800010600;expires=1800010900;nonce=\"202122232425262728292a2b2c2d2e2f\";alg=\"ecdsa-p256-sha256\";keyid=\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "AfmtT0x1DpHafVX2IOkQsrCzpZn2yJ2eUbZTB0OU1uCgexR0X5YsAEiB9Qk29Ae6lhGrSfmehexIjN26T9JJMA==" + }, + { + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\",\"certificate\":\"MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==\",\"name\":\"Atrinik Migration\",\"playersCount\":2,\"version\":\"6.0.0\",\"textComment\":\"Migration fixture\",\"public\":true,\"accessCodeRequired\":true}", + "certificate_der_base64": "MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==", + "created": 1800011200, + "name": "v2-99-stale", + "nonce": "303132333435363738393a3b3c3d3e3f", + "profile": "classic-v2", + "sequence": "99", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694/publish\n\"content-digest\": sha-256=:E1zmfLZvarnPmCAr3tt+Ely7EoWB35guMRvEYsn9QPs=:\n\"content-type\": application/json\n\"atrinik-server-id\": c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\n\"atrinik-publish-sequence\": 99\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800011200;expires=1800011500;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "IX4OhPkSwhsZiM41+FUlNv/gbajIxe07mYYXmK7spwqootCFdbYn0S4Plv1cSKpgvt4MehKQ06E1Ol6S52bhwg==" + }, + { + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\",\"certificate\":\"MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==\",\"name\":\"Atrinik Migration\",\"playersCount\":2,\"version\":\"6.0.0\",\"textComment\":\"Migration fixture\",\"public\":true,\"accessCodeRequired\":true}", + "certificate_der_base64": "MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==", + "created": 1800011800, + "name": "v2-101-retained", + "nonce": "000102030405060708090a0b0c0d0e0f", + "profile": "classic-v2", + "sequence": "101", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694/publish\n\"content-digest\": sha-256=:E1zmfLZvarnPmCAr3tt+Ely7EoWB35guMRvEYsn9QPs=:\n\"content-type\": application/json\n\"atrinik-server-id\": c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\n\"atrinik-publish-sequence\": 101\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800011800;expires=1800012100;nonce=\"000102030405060708090a0b0c0d0e0f\";alg=\"ecdsa-p256-sha256\";keyid=\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "CmbyfZpY5MWLyFyma9wr+jd+gxrIvHVCb+d6vP7MSaDSWBuBrcZAYNKUX4InL03S4lK7Ts+A0KyYrGpZdK+tKA==" + }, + { + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\",\"certificate\":\"MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==\",\"name\":\"Atrinik Migration\",\"playersCount\":2,\"version\":\"6.0.0\",\"textComment\":\"Migration fixture\",\"public\":true,\"accessCodeRequired\":true}", + "certificate_der_base64": "MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==", + "created": 1800012400, + "name": "v2-101-upgrade", + "nonce": "101112131415161718191a1b1c1d1e1f", + "profile": "classic-v2", + "sequence": "101", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694/publish\n\"content-digest\": sha-256=:E1zmfLZvarnPmCAr3tt+Ely7EoWB35guMRvEYsn9QPs=:\n\"content-type\": application/json\n\"atrinik-server-id\": c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\n\"atrinik-publish-sequence\": 101\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800012400;expires=1800012700;nonce=\"101112131415161718191a1b1c1d1e1f\";alg=\"ecdsa-p256-sha256\";keyid=\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "DvJ6a7maylYHWton2oIUsIvdENpypX1sRRLj1ObEjwccRwl+cMFta1zdNMBpk5/1BjizbMHS3ECj2vZ17/EZgg==" + }, + { + "body": "{\"schema\":\"atrinik-classic-publish-v1\",\"serverId\":\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\",\"certificate\":\"MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==\",\"name\":\"Atrinik Migration\",\"playersCount\":2,\"version\":\"5.7.0\",\"textComment\":\"Migration fixture\",\"public\":true,\"passwordRequired\":true}", + "certificate_der_base64": "MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==", + "created": 1800013000, + "name": "v1-102-post-upgrade", + "nonce": "404142434445464748494a4b4c4d4e4f", + "profile": "classic-v1", + "sequence": "102", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v1/classic/servers/c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694/publish\n\"content-digest\": sha-256=:a9IqOwLcZpB4+s5JSwMVl0ShdqPDdsrQbKCXGpPOlDk=:\n\"content-type\": application/json\n\"atrinik-server-id\": c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800013000;expires=1800013300;nonce=\"404142434445464748494a4b4c4d4e4f\";alg=\"ecdsa-p256-sha256\";keyid=\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\";tag=\"atrinik-classic-publish-v1\"", + "signature_base64": "Q6yzOweCXNUPyZr6J4BEuP1DYgcJytmpHOmHyoYwX81eS8FODcTndZjCr4/3U8NAD5LBwN0VJdJuRQhHUuzylw==" + }, + { + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\",\"certificate\":\"MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==\",\"name\":\"Atrinik Migration\",\"playersCount\":2,\"version\":\"6.0.0\",\"textComment\":\"Migration fixture\",\"public\":true,\"accessCodeRequired\":true}", + "certificate_der_base64": "MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==", + "created": 1800013600, + "name": "v2-102-next", + "nonce": "505152535455565758595a5b5c5d5e5f", + "profile": "classic-v2", + "sequence": "102", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694/publish\n\"content-digest\": sha-256=:E1zmfLZvarnPmCAr3tt+Ely7EoWB35guMRvEYsn9QPs=:\n\"content-type\": application/json\n\"atrinik-server-id\": c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800013600;expires=1800013900;nonce=\"505152535455565758595a5b5c5d5e5f\";alg=\"ecdsa-p256-sha256\";keyid=\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "lzLHfNYGTVb5PsfYwvVmQoA4hSepm/lcs07cCQRAxgR6imnaI2xc+vECTfHlqR9RRJGIfXN2k8Sze+D7lpAJLQ==" + }, + { + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\",\"certificate\":\"MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==\",\"name\":\"Atrinik Migration\",\"playersCount\":2,\"version\":\"6.0.0\",\"textComment\":\"Migration fixture\",\"public\":true,\"accessCodeRequired\":true}", + "certificate_der_base64": "MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==", + "created": 1800014200, + "name": "v2-max-minus-one", + "nonce": "808182838485868788898a8b8c8d8e8f", + "profile": "classic-v2", + "sequence": "18446744073709551614", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694/publish\n\"content-digest\": sha-256=:E1zmfLZvarnPmCAr3tt+Ely7EoWB35guMRvEYsn9QPs=:\n\"content-type\": application/json\n\"atrinik-server-id\": c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\n\"atrinik-publish-sequence\": 18446744073709551614\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800014200;expires=1800014500;nonce=\"808182838485868788898a8b8c8d8e8f\";alg=\"ecdsa-p256-sha256\";keyid=\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "HoJR3M3Bsv5axtfTKXm91K6HKfy1oJDWBZ4u2OggYZy4/WMLN0lqJmoWPftUR4yIDvGwaK4H2ba2czNoKbkYXw==" + }, + { + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\",\"certificate\":\"MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==\",\"name\":\"Atrinik Migration\",\"playersCount\":2,\"version\":\"6.0.0\",\"textComment\":\"Migration fixture\",\"public\":true,\"accessCodeRequired\":true}", + "certificate_der_base64": "MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==", + "created": 1800014800, + "name": "v2-max", + "nonce": "606162636465666768696a6b6c6d6e6f", + "profile": "classic-v2", + "sequence": "18446744073709551615", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694/publish\n\"content-digest\": sha-256=:E1zmfLZvarnPmCAr3tt+Ely7EoWB35guMRvEYsn9QPs=:\n\"content-type\": application/json\n\"atrinik-server-id\": c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\n\"atrinik-publish-sequence\": 18446744073709551615\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800014800;expires=1800015100;nonce=\"606162636465666768696a6b6c6d6e6f\";alg=\"ecdsa-p256-sha256\";keyid=\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "HtpMq/nB16y1UakfeHlE3RYAr/K/IQAgqxV6k726HROVkYYOs1hztqDKInZVzie5RF/rhyr+iluluM/Ps5vVDA==" + }, + { + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\",\"certificate\":\"MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==\",\"name\":\"Atrinik Migration\",\"playersCount\":2,\"version\":\"6.0.0\",\"textComment\":\"Migration fixture\",\"public\":true,\"accessCodeRequired\":true}", + "certificate_der_base64": "MIIBaDCCAQ2gAwIBAgIBMzAKBggqhkjOPQQDAjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwHhcNMjYwMTAxMDAwMDAwWhcNMzYwMTAxMDAwMDAwWjAsMSowKAYDVQQDEyFBdHJpbmlrIENsYXNzaWMgbWlncmF0aW9uIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdHSoxBxTmtbzveWyVCcQUjKZqUisbeBnYwcWR0TjMn3LMfS47HJ6XnarpQ25S+fAvyBDaomXo5qjRDnxo+gERoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNJADBGAiEAnuMDu7OIBtQosjPz+jDB0b9Yx30iLXQ6EjZbvi8Wqg4CIQD2AMmack+XPebEAAeArm367WPD2M7DvKz9fNskOV1/ww==", + "created": 1800015400, + "name": "v2-max-exhausted", + "nonce": "707172737475767778797a7b7c7d7e7f", + "profile": "classic-v2", + "sequence": "18446744073709551615", + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694/publish\n\"content-digest\": sha-256=:E1zmfLZvarnPmCAr3tt+Ely7EoWB35guMRvEYsn9QPs=:\n\"content-type\": application/json\n\"atrinik-server-id\": c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\n\"atrinik-publish-sequence\": 18446744073709551615\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800015400;expires=1800015700;nonce=\"707172737475767778797a7b7c7d7e7f\";alg=\"ecdsa-p256-sha256\";keyid=\"c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694\";tag=\"atrinik-classic-publish-v2\"", + "signature_base64": "GkAkdYQPTQV/hPA5NAfsQxUYnfx6KLvaO9yD/Jpp8BVn+cTJ3wehuxsbUl/20IyduB3TWUMv0w5P0P7ZgqiTuA==" + } ] }, "negative": [ { - "name": "v1-schema", "body": "{\"schema\":\"atrinik-classic-publish-v1\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "unsupported_schema" + "error": "unsupported_schema", + "name": "v1-schema" }, { - "name": "legacy-password-field", "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"passwordRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "noncanonical_json" + "error": "noncanonical_json", + "name": "legacy-password-field" }, { - "name": "wrong-key-order", "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"playersCount\":4,\"name\":\"Atrinik Classic\",\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "noncanonical_json" + "error": "noncanonical_json", + "name": "wrong-key-order" }, { - "name": "whitespace", "body": "{ \"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "noncanonical_json" + "error": "noncanonical_json", + "name": "whitespace" }, { - "name": "duplicate-field", "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "noncanonical_json" + "error": "noncanonical_json", + "name": "duplicate-field" }, { - "name": "unknown-field", "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327,\"extra\":true}", - "error": "noncanonical_json" + "error": "noncanonical_json", + "name": "unknown-field" }, { - "name": "malformed-boolean", "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":\"true\",\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "invalid_json" + "error": "invalid_json", + "name": "malformed-boolean" }, { - "name": "missing-policy", "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "noncanonical_json" + "error": "noncanonical_json", + "name": "missing-policy" }, { - "name": "hostname-without-port", "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\"}", - "error": "invalid_endpoint" + "error": "invalid_endpoint", + "name": "hostname-without-port" }, { - "name": "port-without-hostname", "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"port\":13327}", - "error": "invalid_endpoint" + "error": "invalid_endpoint", + "name": "port-without-hostname" }, { - "name": "uppercase-server-id", "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13E46C3B910727029646BA5DB7EE5930080F832AF4D9E29F70CF781250A23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "invalid_identity" + "error": "invalid_identity", + "name": "uppercase-server-id" }, { - "name": "noncanonical-certificate", "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ=\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "invalid_certificate" + "error": "invalid_certificate", + "name": "noncanonical-certificate" }, { - "name": "players-overflow", "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4294967296,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "invalid_players" + "error": "invalid_players", + "name": "players-overflow" }, { - "name": "numeric-hostname", "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"192.0.2.1\",\"port\":13327}", - "error": "invalid_endpoint" + "error": "invalid_endpoint", + "name": "numeric-hostname" }, { - "name": "uppercase-hostname", "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"Play.example.org\",\"port\":13327}", - "error": "invalid_endpoint" + "error": "invalid_endpoint", + "name": "uppercase-hostname" }, { - "name": "control-text", "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"bad\\nvalue\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "error": "invalid_text" + "error": "invalid_text", + "name": "control-text" }, { - "name": "trailing-input", "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}{}", - "error": "noncanonical_json" + "error": "noncanonical_json", + "name": "trailing-input" }, { - "name": "endpoint-alias", "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"endpoint\":{\"hostname\":\"play.example.org\",\"port\":13327}}", - "error": "noncanonical_json" + "error": "noncanonical_json", + "name": "endpoint-alias" } ], "positive": [ { - "name": "public-open-addressless", - "sequence": "101", - "nonce": "202122232425262728292a2b2c2d2e2f", - "created": 1800000000, - "expires": 1800000300, "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":3,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":false}", "content_digest": "sha-256=:Gr86A+BT7xYperp9yC1Dpg2+AJf20bcontFR6hMJvxY=:", + "created": 1800000000, + "expected_state": { + "authenticated_presence": true, + "private_server_control_retained": false, + "public_authorization_active": false, + "public_fields_retained": true, + "public_listing": true, + "rendezvous": "unauthenticated" + }, + "expires": 1800000300, + "name": "public-open-addressless", + "nonce": "202122232425262728292a2b2c2d2e2f", "path": "/v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish", - "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000000;expires=1800000300;nonce=\"202122232425262728292a2b2c2d2e2f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "sequence": "101", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:Gr86A+BT7xYperp9yC1Dpg2+AJf20bcontFR6hMJvxY=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 101\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000000;expires=1800000300;nonce=\"202122232425262728292a2b2c2d2e2f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", "signature_base64": "hn4Vp7Yk9oAcsT/ZJa3JjtPNPkojidIirbvpkugwnUncptdh7TMSkOBesO2BNR2UhWvirP798Nz1YLopF7tHOw==", "signature_header": "atrinik=:hn4Vp7Yk9oAcsT/ZJa3JjtPNPkojidIirbvpkugwnUncptdh7TMSkOBesO2BNR2UhWvirP798Nz1YLopF7tHOw==:", + "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000000;expires=1800000300;nonce=\"202122232425262728292a2b2c2d2e2f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"" + }, + { + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "content_digest": "sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:", + "created": 1800000600, "expected_state": { "authenticated_presence": true, - "public_listing": true, + "private_server_control_retained": false, + "public_authorization_active": true, "public_fields_retained": true, - "rendezvous": "unauthenticated", - "public_authorization_active": false, - "private_server_control_retained": false - } - }, - { + "public_listing": true, + "rendezvous": "proof-required" + }, + "expires": 1800000900, "name": "public-protected-endpoint", - "sequence": "102", "nonce": "303132333435363738393a3b3c3d3e3f", - "created": 1800000600, - "expires": 1800000900, - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", - "content_digest": "sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:", "path": "/v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish", - "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "sequence": "102", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", "signature_header": "atrinik=:1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==:", - "expected_state": { - "authenticated_presence": true, - "public_listing": true, - "public_fields_retained": true, - "rendezvous": "proof-required", - "public_authorization_active": true, - "private_server_control_retained": false - } + "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"" }, { - "name": "private-open-endpoint", - "sequence": "103", - "nonce": "404142434445464748494a4b4c4d4e4f", - "created": 1800001200, - "expires": 1800001500, "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":5,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":false,\"accessCodeRequired\":false,\"hostname\":\"private.example.org\",\"port\":13328}", "content_digest": "sha-256=:vDE4NdX7UjFFd8FvQAw6DJRGcWwkSlDVPQeoJ6WFF4c=:", + "created": 1800001200, + "expected_state": { + "authenticated_presence": true, + "private_server_control_retained": true, + "public_authorization_active": false, + "public_fields_retained": false, + "public_listing": false, + "rendezvous": "not-found" + }, + "expires": 1800001500, + "name": "private-open-endpoint", + "nonce": "404142434445464748494a4b4c4d4e4f", "path": "/v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish", - "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001200;expires=1800001500;nonce=\"404142434445464748494a4b4c4d4e4f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "sequence": "103", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:vDE4NdX7UjFFd8FvQAw6DJRGcWwkSlDVPQeoJ6WFF4c=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 103\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001200;expires=1800001500;nonce=\"404142434445464748494a4b4c4d4e4f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", "signature_base64": "iqRbY+IrGsqLmv33cE2lxOd0cE3dRT70ER5I1W/R+0pqFzpITv4iYu5e0Zpu1GllDo1dRBx3btHG0qFiC/gaEA==", "signature_header": "atrinik=:iqRbY+IrGsqLmv33cE2lxOd0cE3dRT70ER5I1W/R+0pqFzpITv4iYu5e0Zpu1GllDo1dRBx3btHG0qFiC/gaEA==:", + "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001200;expires=1800001500;nonce=\"404142434445464748494a4b4c4d4e4f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"" + }, + { + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":6,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":false,\"accessCodeRequired\":true}", + "content_digest": "sha-256=:s56rYrOnUoGOi8+rE1WX6ztHKCHd7r9kmt8S5DX6uco=:", + "created": 1800001800, "expected_state": { "authenticated_presence": true, - "public_listing": false, - "public_fields_retained": false, - "rendezvous": "not-found", + "private_server_control_retained": true, "public_authorization_active": false, - "private_server_control_retained": true - } - }, - { + "public_fields_retained": false, + "public_listing": false, + "rendezvous": "not-found" + }, + "expires": 1800002100, "name": "private-protected-addressless", - "sequence": "104", "nonce": "505152535455565758595a5b5c5d5e5f", - "created": 1800001800, - "expires": 1800002100, - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":6,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":false,\"accessCodeRequired\":true}", - "content_digest": "sha-256=:s56rYrOnUoGOi8+rE1WX6ztHKCHd7r9kmt8S5DX6uco=:", "path": "/v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish", - "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001800;expires=1800002100;nonce=\"505152535455565758595a5b5c5d5e5f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", + "sequence": "104", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:s56rYrOnUoGOi8+rE1WX6ztHKCHd7r9kmt8S5DX6uco=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 104\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001800;expires=1800002100;nonce=\"505152535455565758595a5b5c5d5e5f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", "signature_base64": "09ZOuTpd+zu1VRmV+Hs12jYGF4bVyxbCgLfNJR0PjsZyDA5bMni8P+4Ni/pYqMs261lxZe43J3iJSiseBxs4ng==", "signature_header": "atrinik=:09ZOuTpd+zu1VRmV+Hs12jYGF4bVyxbCgLfNJR0PjsZyDA5bMni8P+4Ni/pYqMs261lxZe43J3iJSiseBxs4ng==:", - "expected_state": { - "authenticated_presence": true, - "public_listing": false, - "public_fields_retained": false, - "rendezvous": "not-found", - "public_authorization_active": false, - "private_server_control_retained": true - } + "signature_input": "atrinik=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800001800;expires=1800002100;nonce=\"505152535455565758595a5b5c5d5e5f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"" } ], "profile": "classic-v2", "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", "signature_negative": [ { - "name": "body-content-digest", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic mutated\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "error": "invalid_signature", "failure_class": "body_content_digest", + "name": "body-content-digest", "positive": "public-protected-endpoint", - "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic mutated\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:wzSGCMNyq+fY+HvqHsUwSM+VBpBskOYmciOfTpyIGl0=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "error": "invalid_signature" + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==" }, { - "name": "authority", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "error": "invalid_signature", "failure_class": "authority", + "name": "authority", "positive": "public-protected-endpoint", - "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", "signature_base": "\"@method\": POST\n\"@authority\": canary.publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "error": "invalid_signature" + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==" }, { - "name": "sequence", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "error": "invalid_signature", "failure_class": "sequence", + "name": "sequence", "positive": "public-protected-endpoint", - "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 7\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "error": "invalid_signature" + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==" }, { - "name": "v2-signature-at-v1", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "error": "invalid_signature", "failure_class": "route_tag_domain", + "name": "v2-signature-at-v1", "positive": "public-protected-endpoint", - "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v1/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v1\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "error": "invalid_signature" + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==" }, { - "name": "v2-signature-at-game", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "error": "invalid_signature", "failure_class": "route_tag_domain", + "name": "v2-signature-at-game", "positive": "public-protected-endpoint", - "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v1/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-game-publish-v1\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "error": "invalid_signature" + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==" }, { - "name": "wrong-v2-route", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "error": "invalid_signature", "failure_class": "route", + "name": "wrong-v2-route", "positive": "public-protected-endpoint", - "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/server/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "error": "invalid_signature" + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==" }, { - "name": "wrong-v2-tag", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "error": "invalid_signature", "failure_class": "tag", + "name": "wrong-v2-tag", "positive": "public-protected-endpoint", - "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v3\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "error": "invalid_signature" + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==" }, { - "name": "signed-server-id-disagreement", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "error": "invalid_signature", "failure_class": "signed_server_id", + "name": "signed-server-id-disagreement", "positive": "public-protected-endpoint", - "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 03e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "error": "invalid_signature" + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==" }, { - "name": "keyid-disagreement", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "error": "invalid_signature", "failure_class": "keyid", + "name": "keyid-disagreement", "positive": "public-protected-endpoint", - "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"03e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "error": "invalid_signature" + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==" }, { - "name": "certificate-fingerprint-mismatch", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "error": "invalid_identity", "failure_class": "certificate_fingerprint", + "name": "certificate-fingerprint-mismatch", "positive": "public-protected-endpoint", - "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", "server_id": "03e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "error": "invalid_identity" + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==" }, { - "name": "malformed-certificate", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "certificate_der_base64": "bm90IGEgY2VydGlmaWNhdGU=", + "error": "invalid_identity", "failure_class": "certificate_der", + "name": "malformed-certificate", "positive": "public-protected-endpoint", - "certificate_der_base64": "bm90IGEgY2VydGlmaWNhdGU=", "server_id": "47209c9b7af839de69e9a9cd625e9182c1ad63dae79ed88a2dd680fe34218620", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "error": "invalid_identity" + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==" }, { - "name": "non-p256-certificate", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "certificate_der_base64": "MIIB0zCCAVigAwIBAgIUYD79hLD6PhdnB+ntqGm9joG8RgYwCgYIKoZIzj0EAwIwIDEeMBwGA1UEAwwVQXRyaW5pay1QMzg0LW5lZ2F0aXZlMB4XDTI2MDgxNDAyNTI0N1oXDTM2MDgxMTAyNTI0N1owIDEeMBwGA1UEAwwVQXRyaW5pay1QMzg0LW5lZ2F0aXZlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEpkpDP1nwTmqDj1KvqCrULFkMSmv6Tar3SZRws9qrIY1fsGNHTSg36PYZL8RGYvJztpsnyBy+5t9Eff3cVEbWnwhzs78s0+REt4kuKlhYF/YYBK5P/bknu4SWZODHhTslo1MwUTAdBgNVHQ4EFgQUOVhnWI5Z9Ur56JzdIdTr4feKmXMwHwYDVR0jBBgwFoAUOVhnWI5Z9Ur56JzdIdTr4feKmXMwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNpADBmAjEAipJlthtoBzM6gVuIcrGoV72h5MdmefJIi4WMutcSyMHSBS6Xmx1YE3UpeUnEJ9K9AjEAulPPnUMBZ+EmCfTzVGBXqiT66fmoO56+UO5FOVvwar2WwBJuPXAL5BIMFACiXGv3", + "error": "invalid_identity", "failure_class": "certificate_key_type", + "name": "non-p256-certificate", "positive": "public-protected-endpoint", - "certificate_der_base64": "MIIB0zCCAVigAwIBAgIUYD79hLD6PhdnB+ntqGm9joG8RgYwCgYIKoZIzj0EAwIwIDEeMBwGA1UEAwwVQXRyaW5pay1QMzg0LW5lZ2F0aXZlMB4XDTI2MDgxNDAyNTI0N1oXDTM2MDgxMTAyNTI0N1owIDEeMBwGA1UEAwwVQXRyaW5pay1QMzg0LW5lZ2F0aXZlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEpkpDP1nwTmqDj1KvqCrULFkMSmv6Tar3SZRws9qrIY1fsGNHTSg36PYZL8RGYvJztpsnyBy+5t9Eff3cVEbWnwhzs78s0+REt4kuKlhYF/YYBK5P/bknu4SWZODHhTslo1MwUTAdBgNVHQ4EFgQUOVhnWI5Z9Ur56JzdIdTr4feKmXMwHwYDVR0jBBgwFoAUOVhnWI5Z9Ur56JzdIdTr4feKmXMwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNpADBmAjEAipJlthtoBzM6gVuIcrGoV72h5MdmefJIi4WMutcSyMHSBS6Xmx1YE3UpeUnEJ9K9AjEAulPPnUMBZ+EmCfTzVGBXqiT66fmoO56+UO5FOVvwar2WwBJuPXAL5BIMFACiXGv3", "server_id": "42f63fc7bf6955c441bb8d9f00fec3f38cd3f0afd179d59754fd2618766edfee", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==", - "error": "invalid_identity" + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKRIw==" }, { - "name": "malformed-p1363-length", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "error": "invalid_identity", "failure_class": "signature_length", + "name": "malformed-p1363-length", "positive": "public-protected-endpoint", - "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKR", - "error": "invalid_identity" + "signature_base64": "1gJXC5zSAoE9ngEtAC/rIwPdt9+YbW2YwlrLwi9Sj6U9XZdsmTpIMR2/2ASzKuD/P1PusayPzzeVnsj0VFKR" }, { - "name": "zero-p1363-coordinate", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "error": "invalid_signature", "failure_class": "signature_coordinate", + "name": "zero-p1363-coordinate", "positive": "public-protected-endpoint", - "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base64": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", - "error": "invalid_signature" + "signature_base64": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==" }, { - "name": "out-of-range-p1363-coordinate", + "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", + "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", + "error": "invalid_signature", "failure_class": "signature_coordinate", + "name": "out-of-range-p1363-coordinate", "positive": "public-protected-endpoint", - "certificate_der_base64": "MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ", "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "body": "{\"schema\":\"atrinik-classic-publish-v2\",\"serverId\":\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\",\"certificate\":\"MIIBWDCB/6ADAgECAgEhMAoGCCqGSM49BAMCMCUxIzAhBgNVBAMTGkF0cmluaWsgQ2xhc3NpYyB2MiBmaXh0dXJlMB4XDTI2MDEwMTAwMDAwMFoXDTM2MDEwMTAwMDAwMFowJTEjMCEGA1UEAxMaQXRyaW5payBDbGFzc2ljIHYyIGZpeHR1cmUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo4QRR45SdaF7zpESDJmLYTpGJbLuZIR6lYm+D0tiFS4Okj0gskkrVPr5FeHGblhLvUTv1C+GLO08g10p0EmPRoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEAhVxthqGABVxAG0Kqlh0AGWQk74HoNqfr4493srgNBsQCICx9jMsucb8mRVNO1O4E2i2nN6IdtCb6cMNDa/4Y8neZ\",\"name\":\"Atrinik Classic\",\"playersCount\":4,\"version\":\"6.0.0\",\"textComment\":\"Access-code fixture Ω\",\"public\":true,\"accessCodeRequired\":true,\"hostname\":\"play.example.org\",\"port\":13327}", "signature_base": "\"@method\": POST\n\"@authority\": publish.meta.atrinik.org\n\"@path\": /v2/classic/servers/13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809/publish\n\"content-digest\": sha-256=:mBfPpgnbQXGzCdNpaUZtEgJiceUliM4rByayVG5G9VM=:\n\"content-type\": application/json\n\"atrinik-server-id\": 13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\n\"atrinik-publish-sequence\": 102\n\"@signature-params\": (\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\" \"atrinik-server-id\" \"atrinik-publish-sequence\");created=1800000600;expires=1800000900;nonce=\"303132333435363738393a3b3c3d3e3f\";alg=\"ecdsa-p256-sha256\";keyid=\"13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809\";tag=\"atrinik-classic-publish-v2\"", - "signature_base64": "/////////////////////////////////////////////////////////////////////////////////////w==", - "error": "invalid_signature" + "signature_base64": "/////////////////////////////////////////////////////////////////////////////////////w==" } ] } diff --git a/metaserver/classic_publisher_test.go b/metaserver/classic_publisher_test.go index 7dca44b..3fef32a 100644 --- a/metaserver/classic_publisher_test.go +++ b/metaserver/classic_publisher_test.go @@ -77,22 +77,35 @@ type classicV2CrossProfileReplayFixture struct { } type classicV2MigrationState struct { - Mode string `json:"mode"` - HighWaterSequence string `json:"high_water_sequence"` - RetainedNonces []string `json:"retained_nonces"` - V1AuthenticatedPresence bool `json:"v1_authenticated_presence"` - V1ListingPresent bool `json:"v1_listing_present"` - V1RendezvousGenerationPresent bool `json:"v1_rendezvous_generation_present"` - V1ControlsActive bool `json:"v1_controls_active"` - PendingAuthorizations int `json:"pending_authorizations"` - Tickets int `json:"tickets"` - V2Usable bool `json:"v2_usable"` + ServerID string `json:"server_id"` + LastAcceptedEnvelope string `json:"last_accepted_envelope"` + Mode string `json:"mode"` + HighWaterSequence string `json:"high_water_sequence"` + RetainedNonces []string `json:"retained_nonces"` + V1AuthenticatedPresence bool `json:"v1_authenticated_presence"` + V1ListingPresent bool `json:"v1_listing_present"` + V1RendezvousGenerationPresent bool `json:"v1_rendezvous_generation_present"` + V1ControlsActive bool `json:"v1_controls_active"` + PendingAuthorizations int `json:"pending_authorizations"` + Tickets int `json:"tickets"` + V2Usable bool `json:"v2_usable"` + UnaffectedIdentity classicV2UnaffectedIdentityState `json:"unaffected_identity"` +} + +type classicV2UnaffectedIdentityState struct { + ServerID string `json:"server_id"` + Mode string `json:"mode"` + HighWaterSequence string `json:"high_water_sequence"` + V1Presence bool `json:"v1_presence"` + V1Listing bool `json:"v1_listing"` } type classicV2MigrationRequest struct { - Profile string `json:"profile"` - Sequence string `json:"sequence"` - Nonce string `json:"nonce"` + Profile string `json:"profile"` + ServerID string `json:"server_id"` + Sequence string `json:"sequence"` + Nonce string `json:"nonce"` + SignedEnvelope string `json:"signed_envelope"` } type classicV2MigrationExpected struct { @@ -109,6 +122,27 @@ type classicV2MigrationCase struct { Expected classicV2MigrationExpected `json:"expected"` } +type classicV2MigrationSignedEnvelope struct { + Name string `json:"name"` + Profile string `json:"profile"` + ServerID string `json:"server_id"` + CertificateDERBase64 string `json:"certificate_der_base64"` + Sequence string `json:"sequence"` + Nonce string `json:"nonce"` + Created int64 `json:"created"` + Body string `json:"body"` + SignatureBase string `json:"signature_base"` + SignatureBase64 string `json:"signature_base64"` +} + +type classicV2MigrationFailureTransition struct { + Name string `json:"name"` + Case string `json:"case"` + FailurePhase string `json:"failure_phase"` + DurableUpgradeMarker bool `json:"durable_upgrade_marker"` + ExpectedRecovery string `json:"expected_recovery"` +} + type classicV2GlobalRetirementState struct { Mode string `json:"mode"` InFlightV1Commits int `json:"in_flight_v1_commits"` @@ -138,6 +172,18 @@ type classicV2GlobalRetirementFixture struct { SequenceOrNonceConsumed bool `json:"sequence_or_nonce_consumed"` MinimumNextSequence string `json:"minimum_next_sequence"` } `json:"rejected_publish"` + ConcurrentActivation struct { + StartingInFlightV1Commits int `json:"starting_in_flight_v1_commits"` + Behavior string `json:"behavior"` + RacingV1Result string `json:"racing_v1_result"` + ExpectedRecovery string `json:"expected_recovery"` + } `json:"concurrent_activation"` + FailureTransitions []struct { + Name string `json:"name"` + FailurePhase string `json:"failure_phase"` + DurableRetiredMode bool `json:"durable_retired_mode"` + ExpectedRecovery string `json:"expected_recovery"` + } `json:"failure_transitions"` } type classicV2Fixture struct { @@ -163,7 +209,9 @@ type classicV2Fixture struct { Negative []classicV2NegativeFixture `json:"negative"` CrossProfileReplay []classicV2CrossProfileReplayFixture `json:"cross_profile_replay"` Migration struct { - Cases []classicV2MigrationCase `json:"cases"` + SignedEnvelopes []classicV2MigrationSignedEnvelope `json:"signed_envelopes"` + Cases []classicV2MigrationCase `json:"cases"` + FailureTransitions []classicV2MigrationFailureTransition `json:"failure_transitions"` } `json:"migration"` GlobalV1Retirement classicV2GlobalRetirementFixture `json:"global_v1_retirement"` } @@ -343,6 +391,9 @@ func TestClassicV2CrossProfileReplayFixtures(t *testing.T) { t.Fatalf("cross-profile replay count %d, want 2", len(fixture.CrossProfileReplay)) } v1 := loadPublisherFixture(t) + if err := parseFrozenClassicV1Body([]byte(v1.Body)); err != nil { + t.Fatalf("frozen v1 golden was rejected: %v", err) + } seen := make(map[string]bool) for _, test := range fixture.CrossProfileReplay { t.Run(test.Name, func(t *testing.T) { @@ -383,6 +434,9 @@ func TestClassicV2CrossProfileReplayFixtures(t *testing.T) { if _, err := metaserver.ParseClassicV2PublishJSON([]byte(test.Body)); err != nil { t.Fatalf("v2 replay source is not a valid v2 body: %v", err) } + if err := parseFrozenClassicV1Body([]byte(test.Body)); err == nil { + t.Fatal("v2 body was accepted by the frozen v1 codec") + } anchored := false for _, positive := range fixture.Positive { anchored = anchored || (test.Body == positive.Body && test.SourceSignatureBase == positive.SignatureBase && test.SignatureBase64 == positive.SignatureBase64) @@ -401,6 +455,47 @@ func TestClassicV2CrossProfileReplayFixtures(t *testing.T) { } } +func parseFrozenClassicV1Body(body []byte) error { + var schema struct { + Schema string `json:"schema"` + } + if err := json.Unmarshal(body, &schema); err != nil { + return err + } + if schema.Schema != "atrinik-classic-publish-v1" { + return errors.New("unsupported frozen Classic v1 schema") + } + var value struct { + Schema string `json:"schema"` + ServerID string `json:"serverId"` + Certificate string `json:"certificate"` + Name string `json:"name"` + PlayersCount uint64 `json:"playersCount"` + Version string `json:"version"` + TextComment string `json:"textComment"` + Public *bool `json:"public"` + PasswordRequired *bool `json:"passwordRequired"` + Hostname *string `json:"hostname,omitempty"` + Port *uint16 `json:"port,omitempty"` + } + decoder := json.NewDecoder(strings.NewReader(string(body))) + decoder.DisallowUnknownFields() + if err := decoder.Decode(&value); err != nil { + return err + } + if value.Public == nil || value.PasswordRequired == nil || (value.Hostname == nil) != (value.Port == nil) { + return errors.New("incomplete frozen Classic v1 body") + } + encoded, err := json.Marshal(value) + if err != nil { + return err + } + if string(encoded) != string(body) { + return errors.New("noncanonical frozen Classic v1 body") + } + return nil +} + func TestClassicV2PublisherNegativeFixtures(t *testing.T) { fixture := loadClassicV2Fixture(t) if len(fixture.Negative) < 12 { @@ -430,8 +525,34 @@ func TestClassicV2MigrationFixture(t *testing.T) { if len(fixture.Migration.Cases) != 8 { t.Fatalf("migration case count %d, want 8", len(fixture.Migration.Cases)) } + envelopes := make(map[string]classicV2MigrationSignedEnvelope, len(fixture.Migration.SignedEnvelopes)) + for _, envelope := range fixture.Migration.SignedEnvelopes { + if _, exists := envelopes[envelope.Name]; exists { + t.Fatalf("duplicate migration envelope %q", envelope.Name) + } + verifyClassicV2MigrationEnvelope(t, fixture.Authority, envelope) + envelopes[envelope.Name] = envelope + } + if len(envelopes) < len(fixture.Migration.Cases)+1 { + t.Fatal("migration fixture lacks authenticated lineage anchors and requests") + } + cases := make(map[string]classicV2MigrationCase, len(fixture.Migration.Cases)) for _, test := range fixture.Migration.Cases { t.Run(test.Name, func(t *testing.T) { + anchor, ok := envelopes[test.Before.LastAcceptedEnvelope] + if !ok || anchor.ServerID != test.Before.ServerID || anchor.Sequence != test.Before.HighWaterSequence { + t.Fatal("before-state is not bound to its last authenticated envelope") + } + request, ok := envelopes[test.Request.SignedEnvelope] + if !ok || request.Profile != test.Request.Profile || request.ServerID != test.Request.ServerID || + request.Sequence != test.Request.Sequence || request.Nonce != test.Request.Nonce { + t.Fatal("migration request is not bound to its authenticated envelope") + } + if test.Before.UnaffectedIdentity.ServerID == "" || + test.Before.UnaffectedIdentity.ServerID == test.Before.ServerID || + test.Expected.State.UnaffectedIdentity != test.Before.UnaffectedIdentity { + t.Fatal("migration does not preserve a distinct unaffected identity") + } actual := applyClassicV2MigrationFixture(t, test.Before, test.Request) if !reflect.DeepEqual(actual, test.Expected) { t.Fatalf("transition result\n got: %#v\nwant: %#v", actual, test.Expected) @@ -440,6 +561,75 @@ func TestClassicV2MigrationFixture(t *testing.T) { t.Fatal("rejected migration mutated replay or publication state") } }) + cases[test.Name] = test + } + if len(fixture.Migration.FailureTransitions) < 2 { + t.Fatal("migration fixture lacks before-marker rollback and after-marker roll-forward failures") + } + for _, failure := range fixture.Migration.FailureTransitions { + test, ok := cases[failure.Case] + if !ok || test.Name != "higher-v2-upgrade" || failure.FailurePhase == "" { + t.Fatalf("invalid migration failure transition %q", failure.Name) + } + want := test.Before + if failure.DurableUpgradeMarker { + want = test.Expected.State + } + validRecovery := failure.ExpectedRecovery == "exact-before" && reflect.DeepEqual(want, test.Before) + validRecovery = validRecovery || failure.ExpectedRecovery == "exact-after" && reflect.DeepEqual(want, test.Expected.State) + if !validRecovery { + t.Fatalf("failure transition %q has inconsistent recovery", failure.Name) + } + } +} + +func verifyClassicV2MigrationEnvelope(t *testing.T, authority string, envelope classicV2MigrationSignedEnvelope) { + t.Helper() + sequence, err := strconv.ParseUint(envelope.Sequence, 10, 64) + if err != nil { + t.Fatal(err) + } + nonceBytes, err := hex.DecodeString(envelope.Nonce) + if err != nil || len(nonceBytes) != 16 { + t.Fatalf("invalid envelope nonce %q", envelope.Nonce) + } + var nonce [16]byte + copy(nonce[:], nonceBytes) + profile := metaserver.ClassicV1Profile + if envelope.Profile == "classic-v2" { + profile = metaserver.ClassicV2Profile + } else if envelope.Profile != "classic-v1" { + t.Fatalf("invalid envelope profile %q", envelope.Profile) + } + components, err := metaserver.Build(metaserver.Parameters{ + Profile: profile, Authority: authority, ServerID: envelope.ServerID, + Sequence: sequence, Nonce: nonce, Created: envelope.Created, + }, []byte(envelope.Body)) + if err != nil || components.SignatureBase != envelope.SignatureBase { + t.Fatalf("migration envelope did not reconstruct: %v", err) + } + var identity struct { + ServerID string `json:"serverId"` + Certificate string `json:"certificate"` + } + if err := json.Unmarshal([]byte(envelope.Body), &identity); err != nil || + identity.ServerID != envelope.ServerID || identity.Certificate != envelope.CertificateDERBase64 { + t.Fatal("migration body identity does not match its authenticated envelope") + } + if profile == metaserver.ClassicV1Profile { + if err := parseFrozenClassicV1Body([]byte(envelope.Body)); err != nil { + t.Fatalf("invalid v1 migration body: %v", err) + } + } else { + if _, err := metaserver.ParseClassicV2PublishJSON([]byte(envelope.Body)); err != nil { + t.Fatalf("invalid v2 migration body: %v", err) + } + } + if err := metaserver.VerifyCertificateSignature( + decodeBase64(t, envelope.CertificateDERBase64), envelope.ServerID, + envelope.SignatureBase, decodeBase64(t, envelope.SignatureBase64), + ); err != nil { + t.Fatalf("migration envelope signature failed: %v", err) } } @@ -459,6 +649,11 @@ func applyClassicV2MigrationFixture(t *testing.T, before classicV2MigrationState if before.Mode != "classic-v1" && before.Mode != "classic-v2-only" { t.Fatalf("unknown migration mode %q", before.Mode) } + if before.ServerID == "" || request.ServerID != before.ServerID || request.SignedEnvelope == "" { + result := classicV2MigrationExpected{Error: "identity_mismatch", State: before} + result.State.RetainedNonces = append([]string(nil), before.RetainedNonces...) + return result + } for _, nonce := range append(append([]string(nil), before.RetainedNonces...), request.Nonce) { decoded, err := hex.DecodeString(nonce) if err != nil || len(decoded) != 16 { @@ -497,6 +692,7 @@ func applyClassicV2MigrationFixture(t *testing.T, before classicV2MigrationState result.MinimumNextSequence = "" } result.State.HighWaterSequence = request.Sequence + result.State.LastAcceptedEnvelope = request.SignedEnvelope result.State.RetainedNonces = append(result.State.RetainedNonces, request.Nonce) if request.Profile == "classic-v2" { result.State.Mode = "classic-v2-only" @@ -537,6 +733,26 @@ func TestClassicV2GlobalRetirementFixture(t *testing.T) { rejection.Inspection != "none" || rejection.SequenceOrNonceConsumed || rejection.MinimumNextSequence != "" { t.Fatal("global retirement rejection is not fixed, pre-inspection, and non-mutating") } + concurrent := gate.ConcurrentActivation + if concurrent.StartingInFlightV1Commits < 1 || concurrent.Behavior != "exclude-new-v1-and-drain-before-retirement-commit" || + concurrent.RacingV1Result != "profile_retired-after-commit" || concurrent.ExpectedRecovery != "exact-after" { + t.Fatal("global retirement fixture does not serialize a racing v1 commit") + } + if len(gate.FailureTransitions) < 2 { + t.Fatal("global retirement fixture lacks rollback and roll-forward crash phases") + } + for _, failure := range gate.FailureTransitions { + if failure.Name == "" || failure.FailurePhase == "" { + t.Fatal("global failure transition metadata is incomplete") + } + if failure.DurableRetiredMode { + if failure.ExpectedRecovery != "exact-after" { + t.Fatal("durable retired mode did not require roll-forward") + } + } else if failure.ExpectedRecovery != "exact-before" { + t.Fatal("pre-marker failure did not require exact rollback") + } + } } func TestClassicV2PublisherValidationClasses(t *testing.T) { diff --git a/spec/metaserver-publisher.md b/spec/metaserver-publisher.md index 4a8974e..54d7e73 100644 --- a/spec/metaserver-publisher.md +++ b/spec/metaserver-publisher.md @@ -264,6 +264,16 @@ success response and later replay conflicts continue the same exact `minimumNextSequence` lineage. A failed v2 attempt changes neither the v1 state nor the upgrade marker. +Migration is keyed by the authenticated certificate fingerprint, never by a +global Classic slot or a body-only identifier. The shared migration fixture +binds every before-state and request to a complete verified signed envelope and +includes a second identity whose state must remain byte-for-byte unchanged. +Before the durable per-identity v2-only marker, a storage or process failure +recovers the exact v1 before-state; after that marker is durable, recovery +rolls forward to the complete v2 after-state and may not expose any retired v1 +artifact. The receiver serializes a racing publication for the same identity +with this transition; it cannot commit between the marker and retirement. + Classic v1 and v2 bodies, routes, schemas, signature tags, and signatures are mutually non-replayable. A v1 password listing is never emitted in a v2 directory or admitted to a v2 rendezvous room. Game Protocol 1 retains its @@ -313,6 +323,10 @@ restore implementation code but must not reopen v1; recovery after production v2 exposure is roll-forward. Retired per-identity replay records may be contracted only after the mode and v1 tombstones are durable. The normative gate transition and fixed rejection are encoded in the shared fixture. +Its crash-phase vectors require exact rollback before the durable retired-mode +marker and exact roll-forward after it. Its concurrency vector starts with an +in-flight v1 commit and requires receivers to exclude new v1 commits, drain the +existing commit, and only then make the retirement transaction visible. Game Protocol 1 never uses either Classic form, key, route, schema, signature tag, migration marker, or replay lineage. Its `passwordRequired` semantics and From 4f823ac5712af3325929d5a96be900d4479490e3 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Fri, 14 Aug 2026 03:12:11 +0000 Subject: [PATCH 5/6] fix(metaserver): execute retirement recovery vectors --- fixtures/metaserver-classic-publisher-v2.json | 575 ++++++++++++++++-- metaserver/classic_publisher_test.go | 209 ++++++- spec/metaserver-publisher.md | 7 +- 3 files changed, 714 insertions(+), 77 deletions(-) diff --git a/fixtures/metaserver-classic-publisher-v2.json b/fixtures/metaserver-classic-publisher-v2.json index 84b5a62..fd402cb 100644 --- a/fixtures/metaserver-classic-publisher-v2.json +++ b/fixtures/metaserver-classic-publisher-v2.json @@ -37,23 +37,147 @@ "prerequisite": "human-accepted-v5-canaries-and-one-way-alias-cutover" }, "before": { + "identities": [ + { + "authenticated_presence": true, + "controls_active": true, + "high_water_sequence": "100", + "last_accepted_envelope": "v1-100", + "mode": "classic-v1", + "pending_authorizations": 2, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "tickets": 3, + "v2_usable": false + }, + { + "authenticated_presence": true, + "controls_active": true, + "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", + "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "tickets": 1, + "v2_usable": true + } + ], "in_flight_v1_commits": 0, "mode": "classic-v1-accepting", - "pending_authorizations": 3, - "tickets": 4, - "v1_authenticated_presences": 2, - "v1_controls": 2, - "v1_listings": 2, + "pending_authorizations": 2, + "tickets": 3, + "v1_authenticated_presences": 1, + "v1_controls": 1, + "v1_listings": 1, "v1_rendezvous_generations": 1, "v2_state_unchanged": true }, "concurrent_activation": { + "admitted_envelope": "v1-102-post-upgrade", + "admitted_result": { + "body": "{\"status\":\"ok\",\"rendezvousToken\":\"{64-lower-hex}\"}", + "http_status": 200, + "sequence_or_nonce_consumed": true + }, "behavior": "exclude-new-v1-and-drain-before-retirement-commit", - "expected_recovery": "exact-after", - "racing_v1_result": "profile_retired-after-commit", + "excluded_result": { + "body": "{\"error\":{\"code\":\"profile_retired\"}}", + "http_status": 410, + "sequence_or_nonce_consumed": false + }, + "expected_after": { + "identities": [ + { + "authenticated_presence": false, + "controls_active": false, + "high_water_sequence": "102", + "last_accepted_envelope": "v1-102-post-upgrade", + "mode": "classic-v1-retired", + "pending_authorizations": 0, + "public_listing": false, + "rendezvous_generation_present": false, + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f", + "404142434445464748494a4b4c4d4e4f" + ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "tickets": 0, + "v2_usable": false + }, + { + "authenticated_presence": true, + "controls_active": true, + "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", + "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "tickets": 1, + "v2_usable": true + } + ], + "in_flight_v1_commits": 0, + "mode": "classic-v1-retired", + "pending_authorizations": 0, + "rollback_allowed": false, + "tickets": 0, + "v1_authenticated_presences": 0, + "v1_controls": 0, + "v1_listings": 0, + "v1_rendezvous_generations": 0, + "v2_state_unchanged": true + }, "starting_in_flight_v1_commits": 1 }, "expected_after": { + "identities": [ + { + "authenticated_presence": false, + "controls_active": false, + "high_water_sequence": "100", + "last_accepted_envelope": "v1-100", + "mode": "classic-v1-retired", + "pending_authorizations": 0, + "public_listing": false, + "rendezvous_generation_present": false, + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "tickets": 0, + "v2_usable": false + }, + { + "authenticated_presence": true, + "controls_active": true, + "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", + "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "tickets": 1, + "v2_usable": true + } + ], "in_flight_v1_commits": 0, "mode": "classic-v1-retired", "pending_authorizations": 0, @@ -68,15 +192,195 @@ "failure_transitions": [ { "durable_retired_mode": false, - "expected_recovery": "exact-before", + "expected_recovered_state": { + "identities": [ + { + "authenticated_presence": true, + "controls_active": true, + "high_water_sequence": "100", + "last_accepted_envelope": "v1-100", + "mode": "classic-v1", + "pending_authorizations": 2, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "tickets": 3, + "v2_usable": false + }, + { + "authenticated_presence": true, + "controls_active": true, + "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", + "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "tickets": 1, + "v2_usable": true + } + ], + "in_flight_v1_commits": 0, + "mode": "classic-v1-accepting", + "pending_authorizations": 2, + "tickets": 3, + "v1_authenticated_presences": 1, + "v1_controls": 1, + "v1_listings": 1, + "v1_rendezvous_generations": 1, + "v2_state_unchanged": true + }, "failure_phase": "after-drain-before-durable-retired-mode", - "name": "crash-before-retired-mode" + "name": "crash-before-retired-mode", + "partial_state": { + "identities": [ + { + "authenticated_presence": true, + "controls_active": true, + "high_water_sequence": "100", + "last_accepted_envelope": "v1-100", + "mode": "classic-v1", + "pending_authorizations": 2, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "tickets": 3, + "v2_usable": false + }, + { + "authenticated_presence": true, + "controls_active": true, + "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", + "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "tickets": 1, + "v2_usable": true + } + ], + "in_flight_v1_commits": 0, + "mode": "classic-v1-accepting", + "pending_authorizations": 2, + "tickets": 3, + "v1_authenticated_presences": 1, + "v1_controls": 1, + "v1_listings": 1, + "v1_rendezvous_generations": 1, + "v2_state_unchanged": true + } }, { "durable_retired_mode": true, - "expected_recovery": "exact-after", + "expected_recovered_state": { + "identities": [ + { + "authenticated_presence": false, + "controls_active": false, + "high_water_sequence": "100", + "last_accepted_envelope": "v1-100", + "mode": "classic-v1-retired", + "pending_authorizations": 0, + "public_listing": false, + "rendezvous_generation_present": false, + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "tickets": 0, + "v2_usable": false + }, + { + "authenticated_presence": true, + "controls_active": true, + "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", + "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "tickets": 1, + "v2_usable": true + } + ], + "in_flight_v1_commits": 0, + "mode": "classic-v1-retired", + "pending_authorizations": 0, + "rollback_allowed": false, + "tickets": 0, + "v1_authenticated_presences": 0, + "v1_controls": 0, + "v1_listings": 0, + "v1_rendezvous_generations": 0, + "v2_state_unchanged": true + }, "failure_phase": "after-durable-retired-mode-before-tombstones-complete", - "name": "crash-after-retired-mode" + "name": "crash-after-retired-mode", + "partial_state": { + "identities": [ + { + "authenticated_presence": true, + "controls_active": true, + "high_water_sequence": "100", + "last_accepted_envelope": "v1-100", + "mode": "classic-v1", + "pending_authorizations": 2, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "tickets": 3, + "v2_usable": false + }, + { + "authenticated_presence": true, + "controls_active": true, + "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", + "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "tickets": 1, + "v2_usable": true + } + ], + "in_flight_v1_commits": 0, + "mode": "classic-v1-retired", + "pending_authorizations": 2, + "rollback_allowed": false, + "tickets": 3, + "v1_authenticated_presences": 1, + "v1_controls": 1, + "v1_listings": 1, + "v1_rendezvous_generations": 1, + "v2_state_unchanged": true + } } ], "rejected_publish": { @@ -114,11 +418,20 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, "unaffected_identity": { + "authenticated_presence": true, + "controls_active": true, "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "v1_listing": false, - "v1_presence": false + "tickets": 1, + "v2_usable": true }, "v1_authenticated_presence": true, "v1_controls_active": true, @@ -141,11 +454,20 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, "unaffected_identity": { + "authenticated_presence": true, + "controls_active": true, "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "v1_listing": false, - "v1_presence": false + "tickets": 1, + "v2_usable": true }, "v1_authenticated_presence": true, "v1_controls_active": true, @@ -175,11 +497,20 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, "unaffected_identity": { + "authenticated_presence": true, + "controls_active": true, "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "v1_listing": false, - "v1_presence": false + "tickets": 1, + "v2_usable": true }, "v1_authenticated_presence": true, "v1_controls_active": true, @@ -202,11 +533,20 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, "unaffected_identity": { + "authenticated_presence": true, + "controls_active": true, "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "v1_listing": false, - "v1_presence": false + "tickets": 1, + "v2_usable": true }, "v1_authenticated_presence": true, "v1_controls_active": true, @@ -236,11 +576,20 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, "unaffected_identity": { + "authenticated_presence": true, + "controls_active": true, "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "v1_listing": false, - "v1_presence": false + "tickets": 1, + "v2_usable": true }, "v1_authenticated_presence": true, "v1_controls_active": true, @@ -263,11 +612,20 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, "unaffected_identity": { + "authenticated_presence": true, + "controls_active": true, "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "v1_listing": false, - "v1_presence": false + "tickets": 1, + "v2_usable": true }, "v1_authenticated_presence": true, "v1_controls_active": true, @@ -297,11 +655,20 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, "unaffected_identity": { + "authenticated_presence": true, + "controls_active": true, "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "v1_listing": false, - "v1_presence": false + "tickets": 1, + "v2_usable": true }, "v1_authenticated_presence": true, "v1_controls_active": true, @@ -324,11 +691,20 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "authenticated_presence": true, + "controls_active": true, "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "v1_listing": false, - "v1_presence": false + "tickets": 1, + "v2_usable": true }, "v1_authenticated_presence": false, "v1_controls_active": false, @@ -359,11 +735,20 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "authenticated_presence": true, + "controls_active": true, "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "v1_listing": false, - "v1_presence": false + "tickets": 1, + "v2_usable": true }, "v1_authenticated_presence": false, "v1_controls_active": false, @@ -387,11 +772,20 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "authenticated_presence": true, + "controls_active": true, "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "v1_listing": false, - "v1_presence": false + "tickets": 1, + "v2_usable": true }, "v1_authenticated_presence": false, "v1_controls_active": false, @@ -422,11 +816,20 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "authenticated_presence": true, + "controls_active": true, "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "v1_listing": false, - "v1_presence": false + "tickets": 1, + "v2_usable": true }, "v1_authenticated_presence": false, "v1_controls_active": false, @@ -450,11 +853,20 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "authenticated_presence": true, + "controls_active": true, "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "v1_listing": false, - "v1_presence": false + "tickets": 1, + "v2_usable": true }, "v1_authenticated_presence": false, "v1_controls_active": false, @@ -485,11 +897,20 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "authenticated_presence": true, + "controls_active": true, "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "v1_listing": false, - "v1_presence": false + "tickets": 1, + "v2_usable": true }, "v1_authenticated_presence": false, "v1_controls_active": false, @@ -513,11 +934,20 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "authenticated_presence": true, + "controls_active": true, "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "v1_listing": false, - "v1_presence": false + "tickets": 1, + "v2_usable": true }, "v1_authenticated_presence": false, "v1_controls_active": false, @@ -549,11 +979,20 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "authenticated_presence": true, + "controls_active": true, "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "v1_listing": false, - "v1_presence": false + "tickets": 1, + "v2_usable": true }, "v1_authenticated_presence": false, "v1_controls_active": false, @@ -578,11 +1017,20 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "authenticated_presence": true, + "controls_active": true, "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", - "v1_listing": false, - "v1_presence": false + "tickets": 1, + "v2_usable": true }, "v1_authenticated_presence": false, "v1_controls_active": false, @@ -601,6 +1049,47 @@ } } ], + "cross_identity_rejection": { + "expected": { + "accepted": false, + "error": "identity_mismatch", + "minimum_next_sequence": "", + "state": { + "high_water_sequence": "100", + "last_accepted_envelope": "v1-100", + "mode": "classic-v1", + "pending_authorizations": 2, + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "tickets": 3, + "unaffected_identity": { + "authenticated_presence": true, + "controls_active": true, + "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", + "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "tickets": 1, + "v2_usable": true + }, + "v1_authenticated_presence": true, + "v1_controls_active": true, + "v1_listing_present": true, + "v1_rendezvous_generation_present": true, + "v2_usable": false + } + }, + "request_envelope": "positive/public-protected-endpoint", + "target_case": "higher-v2-upgrade" + }, "failure_transitions": [ { "case": "higher-v2-upgrade", diff --git a/metaserver/classic_publisher_test.go b/metaserver/classic_publisher_test.go index 3fef32a..c606067 100644 --- a/metaserver/classic_publisher_test.go +++ b/metaserver/classic_publisher_test.go @@ -93,11 +93,18 @@ type classicV2MigrationState struct { } type classicV2UnaffectedIdentityState struct { - ServerID string `json:"server_id"` - Mode string `json:"mode"` - HighWaterSequence string `json:"high_water_sequence"` - V1Presence bool `json:"v1_presence"` - V1Listing bool `json:"v1_listing"` + ServerID string `json:"server_id"` + LastAcceptedEnvelope string `json:"last_accepted_envelope"` + Mode string `json:"mode"` + HighWaterSequence string `json:"high_water_sequence"` + RetainedNonces []string `json:"retained_nonces"` + AuthenticatedPresence bool `json:"authenticated_presence"` + PublicListing bool `json:"public_listing"` + RendezvousGenerationPresent bool `json:"rendezvous_generation_present"` + ControlsActive bool `json:"controls_active"` + PendingAuthorizations int `json:"pending_authorizations"` + Tickets int `json:"tickets"` + V2Usable bool `json:"v2_usable"` } type classicV2MigrationRequest struct { @@ -143,17 +150,39 @@ type classicV2MigrationFailureTransition struct { ExpectedRecovery string `json:"expected_recovery"` } +type classicV2CrossIdentityRejection struct { + TargetCase string `json:"target_case"` + RequestEnvelope string `json:"request_envelope"` + Expected classicV2MigrationExpected `json:"expected"` +} + +type classicV2GlobalIdentityState struct { + ServerID string `json:"server_id"` + LastAcceptedEnvelope string `json:"last_accepted_envelope"` + Mode string `json:"mode"` + HighWaterSequence string `json:"high_water_sequence"` + RetainedNonces []string `json:"retained_nonces"` + AuthenticatedPresence bool `json:"authenticated_presence"` + PublicListing bool `json:"public_listing"` + RendezvousGenerationPresent bool `json:"rendezvous_generation_present"` + ControlsActive bool `json:"controls_active"` + PendingAuthorizations int `json:"pending_authorizations"` + Tickets int `json:"tickets"` + V2Usable bool `json:"v2_usable"` +} + type classicV2GlobalRetirementState struct { - Mode string `json:"mode"` - InFlightV1Commits int `json:"in_flight_v1_commits"` - V1AuthenticatedPresence int `json:"v1_authenticated_presences"` - V1Listings int `json:"v1_listings"` - V1RendezvousGenerations int `json:"v1_rendezvous_generations"` - V1Controls int `json:"v1_controls"` - PendingAuthorizations int `json:"pending_authorizations"` - Tickets int `json:"tickets"` - V2StateUnchanged bool `json:"v2_state_unchanged"` - RollbackAllowed bool `json:"rollback_allowed"` + Mode string `json:"mode"` + InFlightV1Commits int `json:"in_flight_v1_commits"` + V1AuthenticatedPresence int `json:"v1_authenticated_presences"` + V1Listings int `json:"v1_listings"` + V1RendezvousGenerations int `json:"v1_rendezvous_generations"` + V1Controls int `json:"v1_controls"` + PendingAuthorizations int `json:"pending_authorizations"` + Tickets int `json:"tickets"` + V2StateUnchanged bool `json:"v2_state_unchanged"` + RollbackAllowed bool `json:"rollback_allowed"` + Identities []classicV2GlobalIdentityState `json:"identities"` } type classicV2GlobalRetirementFixture struct { @@ -175,14 +204,25 @@ type classicV2GlobalRetirementFixture struct { ConcurrentActivation struct { StartingInFlightV1Commits int `json:"starting_in_flight_v1_commits"` Behavior string `json:"behavior"` - RacingV1Result string `json:"racing_v1_result"` - ExpectedRecovery string `json:"expected_recovery"` + AdmittedEnvelope string `json:"admitted_envelope"` + AdmittedResult struct { + HTTPStatus int `json:"http_status"` + Body string `json:"body"` + SequenceOrNonceConsumed bool `json:"sequence_or_nonce_consumed"` + } `json:"admitted_result"` + ExcludedResult struct { + HTTPStatus int `json:"http_status"` + Body string `json:"body"` + SequenceOrNonceConsumed bool `json:"sequence_or_nonce_consumed"` + } `json:"excluded_result"` + ExpectedAfter classicV2GlobalRetirementState `json:"expected_after"` } `json:"concurrent_activation"` FailureTransitions []struct { - Name string `json:"name"` - FailurePhase string `json:"failure_phase"` - DurableRetiredMode bool `json:"durable_retired_mode"` - ExpectedRecovery string `json:"expected_recovery"` + Name string `json:"name"` + FailurePhase string `json:"failure_phase"` + DurableRetiredMode bool `json:"durable_retired_mode"` + PartialState classicV2GlobalRetirementState `json:"partial_state"` + ExpectedRecoveredState classicV2GlobalRetirementState `json:"expected_recovered_state"` } `json:"failure_transitions"` } @@ -209,9 +249,10 @@ type classicV2Fixture struct { Negative []classicV2NegativeFixture `json:"negative"` CrossProfileReplay []classicV2CrossProfileReplayFixture `json:"cross_profile_replay"` Migration struct { - SignedEnvelopes []classicV2MigrationSignedEnvelope `json:"signed_envelopes"` - Cases []classicV2MigrationCase `json:"cases"` - FailureTransitions []classicV2MigrationFailureTransition `json:"failure_transitions"` + SignedEnvelopes []classicV2MigrationSignedEnvelope `json:"signed_envelopes"` + Cases []classicV2MigrationCase `json:"cases"` + FailureTransitions []classicV2MigrationFailureTransition `json:"failure_transitions"` + CrossIdentityRejection classicV2CrossIdentityRejection `json:"cross_identity_rejection"` } `json:"migration"` GlobalV1Retirement classicV2GlobalRetirementFixture `json:"global_v1_retirement"` } @@ -537,6 +578,10 @@ func TestClassicV2MigrationFixture(t *testing.T) { t.Fatal("migration fixture lacks authenticated lineage anchors and requests") } cases := make(map[string]classicV2MigrationCase, len(fixture.Migration.Cases)) + positives := make(map[string]classicV2PositiveFixture, len(fixture.Positive)) + for _, positive := range fixture.Positive { + positives["positive/"+positive.Name] = positive + } for _, test := range fixture.Migration.Cases { t.Run(test.Name, func(t *testing.T) { anchor, ok := envelopes[test.Before.LastAcceptedEnvelope] @@ -548,9 +593,15 @@ func TestClassicV2MigrationFixture(t *testing.T) { request.Sequence != test.Request.Sequence || request.Nonce != test.Request.Nonce { t.Fatal("migration request is not bound to its authenticated envelope") } + unaffected := test.Before.UnaffectedIdentity + anchorPositive, anchorOK := positives[unaffected.LastAcceptedEnvelope] + if !anchorOK || unaffected.ServerID != fixture.ServerID || anchorPositive.Sequence != unaffected.HighWaterSequence || + anchorPositive.SignatureBase64 == "" || len(unaffected.RetainedNonces) == 0 { + t.Fatal("unaffected identity is not bound to a complete signed lineage anchor") + } if test.Before.UnaffectedIdentity.ServerID == "" || test.Before.UnaffectedIdentity.ServerID == test.Before.ServerID || - test.Expected.State.UnaffectedIdentity != test.Before.UnaffectedIdentity { + !reflect.DeepEqual(test.Expected.State.UnaffectedIdentity, test.Before.UnaffectedIdentity) { t.Fatal("migration does not preserve a distinct unaffected identity") } actual := applyClassicV2MigrationFixture(t, test.Before, test.Request) @@ -581,6 +632,19 @@ func TestClassicV2MigrationFixture(t *testing.T) { t.Fatalf("failure transition %q has inconsistent recovery", failure.Name) } } + cross := fixture.Migration.CrossIdentityRejection + target, ok := cases[cross.TargetCase] + foreign, foreignOK := positives[cross.RequestEnvelope] + if !ok || !foreignOK { + t.Fatal("cross-identity rejection is not anchored to concrete target and request fixtures") + } + actual := applyClassicV2MigrationFixture(t, target.Before, classicV2MigrationRequest{ + Profile: "classic-v2", ServerID: fixture.ServerID, Sequence: foreign.Sequence, + Nonce: foreign.Nonce, SignedEnvelope: cross.RequestEnvelope, + }) + if !reflect.DeepEqual(actual, cross.Expected) || !reflect.DeepEqual(actual.State, target.Before) { + t.Fatal("foreign signed identity changed the target identity state") + } } func verifyClassicV2MigrationEnvelope(t *testing.T, authority string, envelope classicV2MigrationSignedEnvelope) { @@ -708,14 +772,30 @@ func applyClassicV2MigrationFixture(t *testing.T, before classicV2MigrationState } func TestClassicV2GlobalRetirementFixture(t *testing.T) { - gate := loadClassicV2Fixture(t).GlobalV1Retirement + fixture := loadClassicV2Fixture(t) + gate := fixture.GlobalV1Retirement if gate.Before.Mode != "classic-v1-accepting" || gate.Before.InFlightV1Commits != 0 || gate.Before.V1AuthenticatedPresence == 0 || gate.Before.V1Listings == 0 || gate.Activation.Kind != "authorized-operator-transaction" || gate.Activation.Prerequisite != "human-accepted-v5-canaries-and-one-way-alias-cutover" { t.Fatal("global retirement fixture lacks an explicit drained starting gate and human prerequisite") } - after := gate.Before + anchors := make(map[string]string) + for _, envelope := range fixture.Migration.SignedEnvelopes { + anchors[envelope.Name] = envelope.ServerID + } + for _, positive := range fixture.Positive { + anchors["positive/"+positive.Name] = fixture.ServerID + } + if len(gate.Before.Identities) < 2 { + t.Fatal("global retirement fixture lacks concrete v1 and v2 identities") + } + for _, identity := range gate.Before.Identities { + if anchors[identity.LastAcceptedEnvelope] != identity.ServerID || len(identity.RetainedNonces) == 0 { + t.Fatal("global identity is not bound to a signed replay-lineage anchor") + } + } + after := cloneClassicV2GlobalState(t, gate.Before) after.Mode = "classic-v1-retired" after.V1AuthenticatedPresence = 0 after.V1Listings = 0 @@ -724,9 +804,34 @@ func TestClassicV2GlobalRetirementFixture(t *testing.T) { after.PendingAuthorizations = 0 after.Tickets = 0 after.RollbackAllowed = false - if after != gate.ExpectedAfter || !gate.ExpectedAfter.V2StateUnchanged { + for index := range after.Identities { + identity := &after.Identities[index] + if identity.Mode != "classic-v1" { + continue + } + identity.Mode = "classic-v1-retired" + identity.AuthenticatedPresence = false + identity.PublicListing = false + identity.RendezvousGenerationPresent = false + identity.ControlsActive = false + identity.PendingAuthorizations = 0 + identity.Tickets = 0 + } + if !reflect.DeepEqual(after, gate.ExpectedAfter) || !gate.ExpectedAfter.V2StateUnchanged { t.Fatalf("global retirement transition %#v, want %#v", gate.ExpectedAfter, after) } + for _, beforeIdentity := range gate.Before.Identities { + if beforeIdentity.Mode != "classic-v2-only" { + continue + } + preserved := false + for _, afterIdentity := range gate.ExpectedAfter.Identities { + preserved = preserved || reflect.DeepEqual(beforeIdentity, afterIdentity) + } + if !preserved { + t.Fatal("global retirement changed a concrete v2 identity") + } + } rejection := gate.RejectedPublish if rejection.Profile != "classic-v1" || rejection.HTTPStatus != 410 || rejection.CacheControl != "no-store" || rejection.Body != `{"error":{"code":"profile_retired"}}` || @@ -734,10 +839,30 @@ func TestClassicV2GlobalRetirementFixture(t *testing.T) { t.Fatal("global retirement rejection is not fixed, pre-inspection, and non-mutating") } concurrent := gate.ConcurrentActivation + concurrentAfter := cloneClassicV2GlobalState(t, gate.ExpectedAfter) + for index := range concurrentAfter.Identities { + identity := &concurrentAfter.Identities[index] + if identity.Mode == "classic-v1-retired" { + identity.LastAcceptedEnvelope = concurrent.AdmittedEnvelope + identity.HighWaterSequence = "102" + identity.RetainedNonces = append(identity.RetainedNonces, "404142434445464748494a4b4c4d4e4f") + } + } if concurrent.StartingInFlightV1Commits < 1 || concurrent.Behavior != "exclude-new-v1-and-drain-before-retirement-commit" || - concurrent.RacingV1Result != "profile_retired-after-commit" || concurrent.ExpectedRecovery != "exact-after" { + concurrent.AdmittedEnvelope == "" || concurrent.AdmittedResult.HTTPStatus != 200 || + concurrent.AdmittedResult.Body != `{"status":"ok","rendezvousToken":"{64-lower-hex}"}` || + !concurrent.AdmittedResult.SequenceOrNonceConsumed || concurrent.ExcludedResult.HTTPStatus != 410 || + concurrent.ExcludedResult.Body != rejection.Body || concurrent.ExcludedResult.SequenceOrNonceConsumed || + !reflect.DeepEqual(concurrent.ExpectedAfter, concurrentAfter) { t.Fatal("global retirement fixture does not serialize a racing v1 commit") } + foundAdmitted := false + for _, envelope := range fixture.Migration.SignedEnvelopes { + foundAdmitted = foundAdmitted || (envelope.Name == concurrent.AdmittedEnvelope && envelope.Profile == "classic-v1") + } + if !foundAdmitted { + t.Fatal("admitted in-flight v1 request is not a verified signed envelope") + } if len(gate.FailureTransitions) < 2 { t.Fatal("global retirement fixture lacks rollback and roll-forward crash phases") } @@ -745,16 +870,34 @@ func TestClassicV2GlobalRetirementFixture(t *testing.T) { if failure.Name == "" || failure.FailurePhase == "" { t.Fatal("global failure transition metadata is incomplete") } + want := gate.Before if failure.DurableRetiredMode { - if failure.ExpectedRecovery != "exact-after" { - t.Fatal("durable retired mode did not require roll-forward") + want = gate.ExpectedAfter + if failure.PartialState.Mode != "classic-v1-retired" { + t.Fatal("post-marker crash state lacks the durable retired mode") } - } else if failure.ExpectedRecovery != "exact-before" { - t.Fatal("pre-marker failure did not require exact rollback") + } else if failure.PartialState.Mode != "classic-v1-accepting" { + t.Fatal("pre-marker crash state prematurely exposed retirement") + } + if !reflect.DeepEqual(failure.ExpectedRecoveredState, want) { + t.Fatal("global crash phase did not encode its exact recovered state") } } } +func cloneClassicV2GlobalState(t *testing.T, value classicV2GlobalRetirementState) classicV2GlobalRetirementState { + t.Helper() + encoded, err := json.Marshal(value) + if err != nil { + t.Fatal(err) + } + var cloned classicV2GlobalRetirementState + if err := json.Unmarshal(encoded, &cloned); err != nil { + t.Fatal(err) + } + return cloned +} + func TestClassicV2PublisherValidationClasses(t *testing.T) { fixture := loadClassicV2Fixture(t) base, err := metaserver.ParseClassicV2PublishJSON([]byte(fixture.Positive[1].Body)) diff --git a/spec/metaserver-publisher.md b/spec/metaserver-publisher.md index 54d7e73..549d43a 100644 --- a/spec/metaserver-publisher.md +++ b/spec/metaserver-publisher.md @@ -326,7 +326,12 @@ gate transition and fixed rejection are encoded in the shared fixture. Its crash-phase vectors require exact rollback before the durable retired-mode marker and exact roll-forward after it. Its concurrency vector starts with an in-flight v1 commit and requires receivers to exclude new v1 commits, drain the -existing commit, and only then make the retirement transaction visible. +existing commit, and only then make the retirement transaction visible. A v1 +operation admitted before exclusion completes with its normal authenticated +success response and consumes its sequence and nonce before its resulting v1 +state is retired. A new v1 operation arriving after exclusion receives the +fixed non-consuming HTTP 410 response. These are distinct outcomes and must +never be collapsed into a committed operation reported as `profile_retired`. Game Protocol 1 never uses either Classic form, key, route, schema, signature tag, migration marker, or replay lineage. Its `passwordRequired` semantics and From 4840da01b2179a3850b4ab20c2c9e2bf20c3db09 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Fri, 14 Aug 2026 03:18:30 +0000 Subject: [PATCH 6/6] fix(metaserver): install complete v2 migration state --- fixtures/metaserver-classic-publisher-v2.json | 388 +++++++++++++++++- metaserver/classic_publisher_test.go | 68 ++- spec/metaserver-publisher.md | 12 +- 3 files changed, 450 insertions(+), 18 deletions(-) diff --git a/fixtures/metaserver-classic-publisher-v2.json b/fixtures/metaserver-classic-publisher-v2.json index fd402cb..72cfaa3 100644 --- a/fixtures/metaserver-classic-publisher-v2.json +++ b/fixtures/metaserver-classic-publisher-v2.json @@ -39,6 +39,7 @@ "before": { "identities": [ { + "access_code_required": false, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "100", @@ -46,6 +47,8 @@ "mode": "classic-v1", "pending_authorizations": 2, "public_listing": true, + "published_body_sha256": "", + "rendezvous": "not-found", "rendezvous_generation_present": true, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" @@ -55,6 +58,7 @@ "v2_usable": false }, { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -62,6 +66,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -97,6 +103,7 @@ "expected_after": { "identities": [ { + "access_code_required": false, "authenticated_presence": false, "controls_active": false, "high_water_sequence": "102", @@ -104,6 +111,8 @@ "mode": "classic-v1-retired", "pending_authorizations": 0, "public_listing": false, + "published_body_sha256": "", + "rendezvous": "not-found", "rendezvous_generation_present": false, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f", @@ -114,6 +123,7 @@ "v2_usable": false }, { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -121,6 +131,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -146,6 +158,7 @@ "expected_after": { "identities": [ { + "access_code_required": false, "authenticated_presence": false, "controls_active": false, "high_water_sequence": "100", @@ -153,6 +166,8 @@ "mode": "classic-v1-retired", "pending_authorizations": 0, "public_listing": false, + "published_body_sha256": "", + "rendezvous": "not-found", "rendezvous_generation_present": false, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" @@ -162,6 +177,7 @@ "v2_usable": false }, { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -169,6 +185,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -195,6 +213,7 @@ "expected_recovered_state": { "identities": [ { + "access_code_required": false, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "100", @@ -202,6 +221,8 @@ "mode": "classic-v1", "pending_authorizations": 2, "public_listing": true, + "published_body_sha256": "", + "rendezvous": "not-found", "rendezvous_generation_present": true, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" @@ -211,6 +232,7 @@ "v2_usable": false }, { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -218,6 +240,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -242,6 +266,7 @@ "partial_state": { "identities": [ { + "access_code_required": false, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "100", @@ -249,6 +274,8 @@ "mode": "classic-v1", "pending_authorizations": 2, "public_listing": true, + "published_body_sha256": "", + "rendezvous": "not-found", "rendezvous_generation_present": true, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" @@ -258,6 +285,7 @@ "v2_usable": false }, { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -265,6 +293,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -290,6 +320,7 @@ "expected_recovered_state": { "identities": [ { + "access_code_required": false, "authenticated_presence": false, "controls_active": false, "high_water_sequence": "100", @@ -297,6 +328,8 @@ "mode": "classic-v1-retired", "pending_authorizations": 0, "public_listing": false, + "published_body_sha256": "", + "rendezvous": "not-found", "rendezvous_generation_present": false, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" @@ -306,6 +339,7 @@ "v2_usable": false }, { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -313,6 +347,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -338,6 +374,7 @@ "partial_state": { "identities": [ { + "access_code_required": false, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "100", @@ -345,6 +382,8 @@ "mode": "classic-v1", "pending_authorizations": 2, "public_listing": true, + "published_body_sha256": "", + "rendezvous": "not-found", "rendezvous_generation_present": true, "retained_nonces": [ "000102030405060708090a0b0c0d0e0f" @@ -354,6 +393,7 @@ "v2_usable": false }, { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -361,6 +401,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -418,6 +460,7 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, "unaffected_identity": { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -425,6 +468,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -437,6 +482,13 @@ "v1_controls_active": true, "v1_listing_present": true, "v1_rendezvous_generation_present": true, + "v2_access_code_required": false, + "v2_authenticated_presence": false, + "v2_controls_active": false, + "v2_public_listing": false, + "v2_published_body_sha256": "", + "v2_published_envelope": "", + "v2_rendezvous": "not-found", "v2_usable": false }, "expected": { @@ -454,6 +506,7 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, "unaffected_identity": { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -461,6 +514,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -473,6 +528,13 @@ "v1_controls_active": true, "v1_listing_present": true, "v1_rendezvous_generation_present": true, + "v2_access_code_required": false, + "v2_authenticated_presence": false, + "v2_controls_active": false, + "v2_public_listing": false, + "v2_published_body_sha256": "", + "v2_published_envelope": "", + "v2_rendezvous": "not-found", "v2_usable": false } }, @@ -497,6 +559,7 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, "unaffected_identity": { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -504,6 +567,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -516,6 +581,13 @@ "v1_controls_active": true, "v1_listing_present": true, "v1_rendezvous_generation_present": true, + "v2_access_code_required": false, + "v2_authenticated_presence": false, + "v2_controls_active": false, + "v2_public_listing": false, + "v2_published_body_sha256": "", + "v2_published_envelope": "", + "v2_rendezvous": "not-found", "v2_usable": false }, "expected": { @@ -533,6 +605,7 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, "unaffected_identity": { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -540,6 +613,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -552,6 +627,13 @@ "v1_controls_active": true, "v1_listing_present": true, "v1_rendezvous_generation_present": true, + "v2_access_code_required": false, + "v2_authenticated_presence": false, + "v2_controls_active": false, + "v2_public_listing": false, + "v2_published_body_sha256": "", + "v2_published_envelope": "", + "v2_rendezvous": "not-found", "v2_usable": false } }, @@ -576,6 +658,7 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, "unaffected_identity": { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -583,6 +666,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -595,6 +680,13 @@ "v1_controls_active": true, "v1_listing_present": true, "v1_rendezvous_generation_present": true, + "v2_access_code_required": false, + "v2_authenticated_presence": false, + "v2_controls_active": false, + "v2_public_listing": false, + "v2_published_body_sha256": "", + "v2_published_envelope": "", + "v2_rendezvous": "not-found", "v2_usable": false }, "expected": { @@ -612,6 +704,7 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, "unaffected_identity": { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -619,6 +712,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -631,6 +726,13 @@ "v1_controls_active": true, "v1_listing_present": true, "v1_rendezvous_generation_present": true, + "v2_access_code_required": false, + "v2_authenticated_presence": false, + "v2_controls_active": false, + "v2_public_listing": false, + "v2_published_body_sha256": "", + "v2_published_envelope": "", + "v2_rendezvous": "not-found", "v2_usable": false } }, @@ -655,6 +757,7 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, "unaffected_identity": { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -662,6 +765,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -674,6 +779,13 @@ "v1_controls_active": true, "v1_listing_present": true, "v1_rendezvous_generation_present": true, + "v2_access_code_required": false, + "v2_authenticated_presence": false, + "v2_controls_active": false, + "v2_public_listing": false, + "v2_published_body_sha256": "", + "v2_published_envelope": "", + "v2_rendezvous": "not-found", "v2_usable": false }, "expected": { @@ -691,6 +803,7 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -698,6 +811,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -710,6 +825,13 @@ "v1_controls_active": false, "v1_listing_present": false, "v1_rendezvous_generation_present": false, + "v2_access_code_required": true, + "v2_authenticated_presence": true, + "v2_controls_active": true, + "v2_public_listing": true, + "v2_published_body_sha256": "135ce67cb66f6ab9cf98202bdedb7e125cbb128581df982e311bc462c9fd40fb", + "v2_published_envelope": "v2-101-upgrade", + "v2_rendezvous": "proof-required", "v2_usable": true } }, @@ -735,6 +857,7 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -742,6 +865,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -754,6 +879,13 @@ "v1_controls_active": false, "v1_listing_present": false, "v1_rendezvous_generation_present": false, + "v2_access_code_required": true, + "v2_authenticated_presence": true, + "v2_controls_active": true, + "v2_public_listing": true, + "v2_published_body_sha256": "135ce67cb66f6ab9cf98202bdedb7e125cbb128581df982e311bc462c9fd40fb", + "v2_published_envelope": "v2-101-upgrade", + "v2_rendezvous": "proof-required", "v2_usable": true }, "expected": { @@ -772,6 +904,7 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -779,6 +912,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -791,6 +926,13 @@ "v1_controls_active": false, "v1_listing_present": false, "v1_rendezvous_generation_present": false, + "v2_access_code_required": true, + "v2_authenticated_presence": true, + "v2_controls_active": true, + "v2_public_listing": true, + "v2_published_body_sha256": "135ce67cb66f6ab9cf98202bdedb7e125cbb128581df982e311bc462c9fd40fb", + "v2_published_envelope": "v2-101-upgrade", + "v2_rendezvous": "proof-required", "v2_usable": true } }, @@ -816,6 +958,7 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -823,6 +966,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -835,6 +980,13 @@ "v1_controls_active": false, "v1_listing_present": false, "v1_rendezvous_generation_present": false, + "v2_access_code_required": true, + "v2_authenticated_presence": true, + "v2_controls_active": true, + "v2_public_listing": true, + "v2_published_body_sha256": "135ce67cb66f6ab9cf98202bdedb7e125cbb128581df982e311bc462c9fd40fb", + "v2_published_envelope": "v2-101-upgrade", + "v2_rendezvous": "proof-required", "v2_usable": true }, "expected": { @@ -853,6 +1005,7 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -860,6 +1013,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -872,6 +1027,13 @@ "v1_controls_active": false, "v1_listing_present": false, "v1_rendezvous_generation_present": false, + "v2_access_code_required": true, + "v2_authenticated_presence": true, + "v2_controls_active": true, + "v2_public_listing": true, + "v2_published_body_sha256": "135ce67cb66f6ab9cf98202bdedb7e125cbb128581df982e311bc462c9fd40fb", + "v2_published_envelope": "v2-102-next", + "v2_rendezvous": "proof-required", "v2_usable": true } }, @@ -897,6 +1059,7 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -904,6 +1067,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -916,6 +1081,13 @@ "v1_controls_active": false, "v1_listing_present": false, "v1_rendezvous_generation_present": false, + "v2_access_code_required": true, + "v2_authenticated_presence": true, + "v2_controls_active": true, + "v2_public_listing": true, + "v2_published_body_sha256": "135ce67cb66f6ab9cf98202bdedb7e125cbb128581df982e311bc462c9fd40fb", + "v2_published_envelope": "v2-max-minus-one", + "v2_rendezvous": "proof-required", "v2_usable": true }, "expected": { @@ -934,6 +1106,7 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -941,6 +1114,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -953,6 +1128,13 @@ "v1_controls_active": false, "v1_listing_present": false, "v1_rendezvous_generation_present": false, + "v2_access_code_required": true, + "v2_authenticated_presence": true, + "v2_controls_active": true, + "v2_public_listing": true, + "v2_published_body_sha256": "135ce67cb66f6ab9cf98202bdedb7e125cbb128581df982e311bc462c9fd40fb", + "v2_published_envelope": "v2-max", + "v2_rendezvous": "proof-required", "v2_usable": true } }, @@ -979,6 +1161,7 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -986,6 +1169,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -998,6 +1183,13 @@ "v1_controls_active": false, "v1_listing_present": false, "v1_rendezvous_generation_present": false, + "v2_access_code_required": true, + "v2_authenticated_presence": true, + "v2_controls_active": true, + "v2_public_listing": true, + "v2_published_body_sha256": "135ce67cb66f6ab9cf98202bdedb7e125cbb128581df982e311bc462c9fd40fb", + "v2_published_envelope": "v2-max", + "v2_rendezvous": "proof-required", "v2_usable": true }, "expected": { @@ -1017,6 +1209,7 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 0, "unaffected_identity": { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -1024,6 +1217,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -1036,6 +1231,13 @@ "v1_controls_active": false, "v1_listing_present": false, "v1_rendezvous_generation_present": false, + "v2_access_code_required": true, + "v2_authenticated_presence": true, + "v2_controls_active": true, + "v2_public_listing": true, + "v2_published_body_sha256": "135ce67cb66f6ab9cf98202bdedb7e125cbb128581df982e311bc462c9fd40fb", + "v2_published_envelope": "v2-max", + "v2_rendezvous": "proof-required", "v2_usable": true } }, @@ -1065,6 +1267,7 @@ "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", "tickets": 3, "unaffected_identity": { + "access_code_required": true, "authenticated_presence": true, "controls_active": true, "high_water_sequence": "102", @@ -1072,6 +1275,8 @@ "mode": "classic-v2-only", "pending_authorizations": 1, "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", "rendezvous_generation_present": true, "retained_nonces": [ "303132333435363738393a3b3c3d3e3f" @@ -1084,6 +1289,13 @@ "v1_controls_active": true, "v1_listing_present": true, "v1_rendezvous_generation_present": true, + "v2_access_code_required": false, + "v2_authenticated_presence": false, + "v2_controls_active": false, + "v2_public_listing": false, + "v2_published_body_sha256": "", + "v2_published_envelope": "", + "v2_rendezvous": "not-found", "v2_usable": false } }, @@ -1094,16 +1306,184 @@ { "case": "higher-v2-upgrade", "durable_upgrade_marker": false, - "expected_recovery": "exact-before", + "expected_recovered_state": { + "high_water_sequence": "100", + "last_accepted_envelope": "v1-100", + "mode": "classic-v1", + "pending_authorizations": 2, + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "tickets": 3, + "unaffected_identity": { + "access_code_required": true, + "authenticated_presence": true, + "controls_active": true, + "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", + "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "tickets": 1, + "v2_usable": true + }, + "v1_authenticated_presence": true, + "v1_controls_active": true, + "v1_listing_present": true, + "v1_rendezvous_generation_present": true, + "v2_access_code_required": false, + "v2_authenticated_presence": false, + "v2_controls_active": false, + "v2_public_listing": false, + "v2_published_body_sha256": "", + "v2_published_envelope": "", + "v2_rendezvous": "not-found", + "v2_usable": false + }, "failure_phase": "before-durable-v2-only-marker", - "name": "crash-before-upgrade-marker" + "name": "crash-before-upgrade-marker", + "partial_state": { + "high_water_sequence": "100", + "last_accepted_envelope": "v1-100", + "mode": "classic-v1", + "pending_authorizations": 2, + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f" + ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "tickets": 3, + "unaffected_identity": { + "access_code_required": true, + "authenticated_presence": true, + "controls_active": true, + "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", + "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "tickets": 1, + "v2_usable": true + }, + "v1_authenticated_presence": true, + "v1_controls_active": true, + "v1_listing_present": true, + "v1_rendezvous_generation_present": true, + "v2_access_code_required": false, + "v2_authenticated_presence": false, + "v2_controls_active": false, + "v2_public_listing": false, + "v2_published_body_sha256": "", + "v2_published_envelope": "", + "v2_rendezvous": "not-found", + "v2_usable": false + } }, { "case": "higher-v2-upgrade", "durable_upgrade_marker": true, - "expected_recovery": "exact-after", + "expected_recovered_state": { + "high_water_sequence": "101", + "last_accepted_envelope": "v2-101-upgrade", + "mode": "classic-v2-only", + "pending_authorizations": 0, + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f", + "101112131415161718191a1b1c1d1e1f" + ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "tickets": 0, + "unaffected_identity": { + "access_code_required": true, + "authenticated_presence": true, + "controls_active": true, + "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", + "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "tickets": 1, + "v2_usable": true + }, + "v1_authenticated_presence": false, + "v1_controls_active": false, + "v1_listing_present": false, + "v1_rendezvous_generation_present": false, + "v2_access_code_required": true, + "v2_authenticated_presence": true, + "v2_controls_active": true, + "v2_public_listing": true, + "v2_published_body_sha256": "135ce67cb66f6ab9cf98202bdedb7e125cbb128581df982e311bc462c9fd40fb", + "v2_published_envelope": "v2-101-upgrade", + "v2_rendezvous": "proof-required", + "v2_usable": true + }, "failure_phase": "after-durable-v2-only-marker-before-retirement-complete", - "name": "crash-after-upgrade-marker" + "name": "crash-after-upgrade-marker", + "partial_state": { + "high_water_sequence": "101", + "last_accepted_envelope": "v2-101-upgrade", + "mode": "classic-v2-only", + "pending_authorizations": 2, + "retained_nonces": [ + "000102030405060708090a0b0c0d0e0f", + "101112131415161718191a1b1c1d1e1f" + ], + "server_id": "c57c33b5b8f51d80a8d5d24faa155f4b1839f446d8bdc66884e9bd355bb5a694", + "tickets": 3, + "unaffected_identity": { + "access_code_required": true, + "authenticated_presence": true, + "controls_active": true, + "high_water_sequence": "102", + "last_accepted_envelope": "positive/public-protected-endpoint", + "mode": "classic-v2-only", + "pending_authorizations": 1, + "public_listing": true, + "published_body_sha256": "9817cfa609db4171b309d36969466d12026271e52588ce2b0726b2546e46f553", + "rendezvous": "proof-required", + "rendezvous_generation_present": true, + "retained_nonces": [ + "303132333435363738393a3b3c3d3e3f" + ], + "server_id": "13e46c3b910727029646ba5db7ee5930080f832af4d9e29f70cf781250a23809", + "tickets": 1, + "v2_usable": true + }, + "v1_authenticated_presence": true, + "v1_controls_active": true, + "v1_listing_present": true, + "v1_rendezvous_generation_present": true, + "v2_access_code_required": true, + "v2_authenticated_presence": true, + "v2_controls_active": true, + "v2_public_listing": true, + "v2_published_body_sha256": "135ce67cb66f6ab9cf98202bdedb7e125cbb128581df982e311bc462c9fd40fb", + "v2_published_envelope": "v2-101-upgrade", + "v2_rendezvous": "proof-required", + "v2_usable": true + } } ], "signed_envelopes": [ diff --git a/metaserver/classic_publisher_test.go b/metaserver/classic_publisher_test.go index c606067..968abba 100644 --- a/metaserver/classic_publisher_test.go +++ b/metaserver/classic_publisher_test.go @@ -89,6 +89,13 @@ type classicV2MigrationState struct { PendingAuthorizations int `json:"pending_authorizations"` Tickets int `json:"tickets"` V2Usable bool `json:"v2_usable"` + V2AuthenticatedPresence bool `json:"v2_authenticated_presence"` + V2PublicListing bool `json:"v2_public_listing"` + V2AccessCodeRequired bool `json:"v2_access_code_required"` + V2Rendezvous string `json:"v2_rendezvous"` + V2PublishedBodySHA256 string `json:"v2_published_body_sha256"` + V2PublishedEnvelope string `json:"v2_published_envelope"` + V2ControlsActive bool `json:"v2_controls_active"` UnaffectedIdentity classicV2UnaffectedIdentityState `json:"unaffected_identity"` } @@ -105,6 +112,9 @@ type classicV2UnaffectedIdentityState struct { PendingAuthorizations int `json:"pending_authorizations"` Tickets int `json:"tickets"` V2Usable bool `json:"v2_usable"` + AccessCodeRequired bool `json:"access_code_required"` + Rendezvous string `json:"rendezvous"` + PublishedBodySHA256 string `json:"published_body_sha256"` } type classicV2MigrationRequest struct { @@ -143,11 +153,12 @@ type classicV2MigrationSignedEnvelope struct { } type classicV2MigrationFailureTransition struct { - Name string `json:"name"` - Case string `json:"case"` - FailurePhase string `json:"failure_phase"` - DurableUpgradeMarker bool `json:"durable_upgrade_marker"` - ExpectedRecovery string `json:"expected_recovery"` + Name string `json:"name"` + Case string `json:"case"` + FailurePhase string `json:"failure_phase"` + DurableUpgradeMarker bool `json:"durable_upgrade_marker"` + PartialState classicV2MigrationState `json:"partial_state"` + ExpectedRecoveredState classicV2MigrationState `json:"expected_recovered_state"` } type classicV2CrossIdentityRejection struct { @@ -169,6 +180,9 @@ type classicV2GlobalIdentityState struct { PendingAuthorizations int `json:"pending_authorizations"` Tickets int `json:"tickets"` V2Usable bool `json:"v2_usable"` + AccessCodeRequired bool `json:"access_code_required"` + Rendezvous string `json:"rendezvous"` + PublishedBodySHA256 string `json:"published_body_sha256"` } type classicV2GlobalRetirementState struct { @@ -595,8 +609,11 @@ func TestClassicV2MigrationFixture(t *testing.T) { } unaffected := test.Before.UnaffectedIdentity anchorPositive, anchorOK := positives[unaffected.LastAcceptedEnvelope] + unaffectedDigest := sha256.Sum256([]byte(anchorPositive.Body)) if !anchorOK || unaffected.ServerID != fixture.ServerID || anchorPositive.Sequence != unaffected.HighWaterSequence || - anchorPositive.SignatureBase64 == "" || len(unaffected.RetainedNonces) == 0 { + anchorPositive.SignatureBase64 == "" || len(unaffected.RetainedNonces) == 0 || + unaffected.PublishedBodySHA256 != hex.EncodeToString(unaffectedDigest[:]) || + !unaffected.AccessCodeRequired || unaffected.Rendezvous != "proof-required" { t.Fatal("unaffected identity is not bound to a complete signed lineage anchor") } if test.Before.UnaffectedIdentity.ServerID == "" || @@ -604,7 +621,7 @@ func TestClassicV2MigrationFixture(t *testing.T) { !reflect.DeepEqual(test.Expected.State.UnaffectedIdentity, test.Before.UnaffectedIdentity) { t.Fatal("migration does not preserve a distinct unaffected identity") } - actual := applyClassicV2MigrationFixture(t, test.Before, test.Request) + actual := applyClassicV2MigrationFixture(t, test.Before, test.Request, envelopes) if !reflect.DeepEqual(actual, test.Expected) { t.Fatalf("transition result\n got: %#v\nwant: %#v", actual, test.Expected) } @@ -626,11 +643,15 @@ func TestClassicV2MigrationFixture(t *testing.T) { if failure.DurableUpgradeMarker { want = test.Expected.State } - validRecovery := failure.ExpectedRecovery == "exact-before" && reflect.DeepEqual(want, test.Before) - validRecovery = validRecovery || failure.ExpectedRecovery == "exact-after" && reflect.DeepEqual(want, test.Expected.State) - if !validRecovery { + if !reflect.DeepEqual(failure.ExpectedRecoveredState, want) { t.Fatalf("failure transition %q has inconsistent recovery", failure.Name) } + if failure.DurableUpgradeMarker && failure.PartialState.Mode != "classic-v2-only" { + t.Fatalf("post-marker partial state %q lacks v2-only mode", failure.Name) + } + if !failure.DurableUpgradeMarker && !reflect.DeepEqual(failure.PartialState, test.Before) { + t.Fatalf("pre-marker partial state %q changed the before-state", failure.Name) + } } cross := fixture.Migration.CrossIdentityRejection target, ok := cases[cross.TargetCase] @@ -641,7 +662,7 @@ func TestClassicV2MigrationFixture(t *testing.T) { actual := applyClassicV2MigrationFixture(t, target.Before, classicV2MigrationRequest{ Profile: "classic-v2", ServerID: fixture.ServerID, Sequence: foreign.Sequence, Nonce: foreign.Nonce, SignedEnvelope: cross.RequestEnvelope, - }) + }, envelopes) if !reflect.DeepEqual(actual, cross.Expected) || !reflect.DeepEqual(actual.State, target.Before) { t.Fatal("foreign signed identity changed the target identity state") } @@ -697,7 +718,7 @@ func verifyClassicV2MigrationEnvelope(t *testing.T, authority string, envelope c } } -func applyClassicV2MigrationFixture(t *testing.T, before classicV2MigrationState, request classicV2MigrationRequest) classicV2MigrationExpected { +func applyClassicV2MigrationFixture(t *testing.T, before classicV2MigrationState, request classicV2MigrationRequest, envelopes map[string]classicV2MigrationSignedEnvelope) classicV2MigrationExpected { t.Helper() highWater, err := strconv.ParseUint(before.HighWaterSequence, 10, 64) if err != nil { @@ -759,6 +780,22 @@ func applyClassicV2MigrationFixture(t *testing.T, before classicV2MigrationState result.State.LastAcceptedEnvelope = request.SignedEnvelope result.State.RetainedNonces = append(result.State.RetainedNonces, request.Nonce) if request.Profile == "classic-v2" { + envelope, ok := envelopes[request.SignedEnvelope] + if !ok { + t.Fatalf("accepted migration lacks signed envelope %q", request.SignedEnvelope) + } + published, err := metaserver.ParseClassicV2PublishJSON([]byte(envelope.Body)) + if err != nil { + t.Fatalf("accepted migration has invalid v2 body: %v", err) + } + bodyDigest := sha256.Sum256([]byte(envelope.Body)) + rendezvous := "not-found" + if published.Public { + rendezvous = "unauthenticated" + if published.AccessCodeRequired { + rendezvous = "proof-required" + } + } result.State.Mode = "classic-v2-only" result.State.V1AuthenticatedPresence = false result.State.V1ListingPresent = false @@ -767,6 +804,13 @@ func applyClassicV2MigrationFixture(t *testing.T, before classicV2MigrationState result.State.PendingAuthorizations = 0 result.State.Tickets = 0 result.State.V2Usable = true + result.State.V2AuthenticatedPresence = true + result.State.V2PublicListing = published.Public + result.State.V2AccessCodeRequired = published.AccessCodeRequired + result.State.V2Rendezvous = rendezvous + result.State.V2PublishedBodySHA256 = hex.EncodeToString(bodyDigest[:]) + result.State.V2PublishedEnvelope = request.SignedEnvelope + result.State.V2ControlsActive = true } return result } diff --git a/spec/metaserver-publisher.md b/spec/metaserver-publisher.md index 549d43a..7756903 100644 --- a/spec/metaserver-publisher.md +++ b/spec/metaserver-publisher.md @@ -268,11 +268,19 @@ Migration is keyed by the authenticated certificate fingerprint, never by a global Classic slot or a body-only identifier. The shared migration fixture binds every before-state and request to a complete verified signed envelope and includes a second identity whose state must remain byte-for-byte unchanged. +The accepted after-state is derived from the verified v2 body and records its +authenticated presence, public/private listing effect, configured access-code +Boolean, resulting rendezvous mode, server-control state, exact signed-envelope +reference, and SHA-256 digest of the complete published body; `v2 usable` alone +is not a conforming installed state. The unaffected identity carries the same +complete digest-bound replay and publication state. Before the durable per-identity v2-only marker, a storage or process failure recovers the exact v1 before-state; after that marker is durable, recovery rolls forward to the complete v2 after-state and may not expose any retired v1 -artifact. The receiver serializes a racing publication for the same identity -with this transition; it cannot commit between the marker and retirement. +artifact. The fixture provides concrete partial persisted states and exact +recovered states on both sides of that marker. The receiver serializes a racing +publication for the same identity with this transition; it cannot commit +between the marker and retirement. Classic v1 and v2 bodies, routes, schemas, signature tags, and signatures are mutually non-replayable. A v1 password listing is never emitted in a v2