冻结 GameArtifactManifest/GameSourceArchive 契约和 V34 pending/committed 状态记录,增加专用 writer 的全量回读物化校验,并让 Runtime API 在消费和发布前对账 committed、GamePackage checksum 与实际 engineBundle hash。隔离 staging 已验证真实消费拒绝未提交版本;源码桶权限仍保持 fail-closed,正式金标不切换。
108 lines
5.3 KiB
JSON
108 lines
5.3 KiB
JSON
{
|
||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||
"$id": "https://wanxiang.ai/contracts/game-artifact-manifest.schema.json",
|
||
"title": "GameArtifactManifest",
|
||
"description": "具体游戏版本的对象存储索引。它不是 GamePackage/1 运行时 manifest;运行时仍读取同一版本前缀下的 manifest.json,状态由 game_runtime_package.status 管理。实际游戏内容只存 MinIO/S3。",
|
||
"type": "object",
|
||
"additionalProperties": false,
|
||
"allOf": [
|
||
{
|
||
"if": {
|
||
"properties": {
|
||
"sourceRevision": {
|
||
"properties": { "provider": { "const": "tier2_source_project" } }
|
||
}
|
||
}
|
||
},
|
||
"then": {
|
||
"properties": { "packageType": { "const": "phaser" } }
|
||
}
|
||
}
|
||
],
|
||
"required": [
|
||
"schemaVersion",
|
||
"tenantId",
|
||
"gameId",
|
||
"versionId",
|
||
"packageType",
|
||
"keyPrefix",
|
||
"sourceRevision",
|
||
"runtimeManifest",
|
||
"objects",
|
||
"totals",
|
||
"assetHash",
|
||
"bundleHash",
|
||
"manifestHash"
|
||
],
|
||
"properties": {
|
||
"schemaVersion": { "const": "GameArtifactManifest/1", "type": "string" },
|
||
"tenantId": { "type": "string", "minLength": 1, "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$" },
|
||
"gameId": { "description": "project.game_project.id 的十进制字符串", "type": "string", "pattern": "^[1-9][0-9]*$" },
|
||
"versionId": { "description": "project.game_version.id 的十进制字符串", "type": "string", "pattern": "^[1-9][0-9]*$" },
|
||
"packageType": { "type": "string", "enum": ["phaser", "littlejs", "canvas"] },
|
||
"keyPrefix": { "type": "string", "pattern": "^tenants/[A-Za-z0-9][A-Za-z0-9._-]*/games/[1-9][0-9]*/versions/[1-9][0-9]*$" },
|
||
"sourceRevision": {
|
||
"description": "源工程由既有 Tier2 SourceProjectStore 或引擎无关 GameSourceArchive 持有;运行制品只绑定其不可变身份,不复制源文件。",
|
||
"type": "object",
|
||
"additionalProperties": false,
|
||
"required": ["provider", "gameId", "revisionId", "manifestRef", "sourceHash", "objects", "bytes"],
|
||
"properties": {
|
||
"provider": { "type": "string", "enum": ["tier2_source_project", "game_source_archive"] },
|
||
"gameId": { "type": "string", "minLength": 1, "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$" },
|
||
"revisionId": { "type": "string", "minLength": 1, "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$" },
|
||
"manifestRef": { "type": "string", "minLength": 1, "maxLength": 1024, "pattern": "^(tier2-source-project|game-source-archive):[^:]+:[^:]+$" },
|
||
"sourceHash": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
||
"objects": { "type": "integer", "minimum": 1, "maximum": 10000 },
|
||
"bytes": { "type": "integer", "minimum": 1 }
|
||
}
|
||
},
|
||
"runtimeManifest": {
|
||
"description": "现有 GamePackage/1 manifest.json 的稳定对象身份;不能把 artifact-manifest.json 当作该运行时清单。",
|
||
"type": "object",
|
||
"additionalProperties": false,
|
||
"required": ["store", "key", "sourcePath", "bytes", "sha256", "mime"],
|
||
"properties": {
|
||
"store": { "const": "artifact", "type": "string" },
|
||
"key": { "type": "string", "minLength": 1 },
|
||
"sourcePath": { "type": "string", "minLength": 1, "pattern": "^(?!/)(?!.*(?:^|/)\\.{1,2}(?:/|$))(?!.*\\\\).+$" },
|
||
"bytes": { "type": "integer", "minimum": 0, "maximum": 104857600 },
|
||
"sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
||
"mime": { "type": "string", "minLength": 1 }
|
||
}
|
||
},
|
||
"objects": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "object",
|
||
"additionalProperties": false,
|
||
"required": ["category", "store", "path", "sourcePath", "key", "bytes", "sha256", "mime"],
|
||
"properties": {
|
||
"category": { "type": "string", "enum": ["asset", "runtime", "evidence"] },
|
||
"store": { "type": "string", "enum": ["artifact", "evidence"] },
|
||
"path": { "type": "string", "minLength": 1, "pattern": "^(assets|runtime|evidence)/[^/].*" },
|
||
"sourcePath": { "type": "string", "minLength": 1, "pattern": "^(?!/)(?!.*(?:^|/)\\.{1,2}(?:/|$))(?!.*\\\\).+$" },
|
||
"key": { "type": "string", "minLength": 1, "pattern": "^tenants/.+/games/[1-9][0-9]*/versions/[1-9][0-9]*/(assets|runtime|evidence)/[^/].*" },
|
||
"bytes": { "type": "integer", "minimum": 0, "maximum": 104857600 },
|
||
"sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
||
"mime": { "type": "string", "minLength": 1 }
|
||
}
|
||
}
|
||
},
|
||
"totals": {
|
||
"type": "object",
|
||
"additionalProperties": false,
|
||
"required": ["objects", "bytes", "assetBytes", "runtimeBytes", "evidenceBytes"],
|
||
"properties": {
|
||
"objects": { "type": "integer", "minimum": 0 },
|
||
"bytes": { "type": "integer", "minimum": 0 },
|
||
"assetBytes": { "type": "integer", "minimum": 0 },
|
||
"runtimeBytes": { "type": "integer", "minimum": 0 },
|
||
"evidenceBytes": { "type": "integer", "minimum": 0 }
|
||
}
|
||
},
|
||
"assetHash": { "description": "SHA-256(GameArtifactTreeHash/1\\0 + canonical JSON entries)", "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
||
"bundleHash": { "type": ["string", "null"], "pattern": "^[a-f0-9]{64}$" },
|
||
"manifestHash": { "description": "SHA-256(GameArtifactManifest/1\\0 + 去除 manifestHash 后的 UTF-8、NFC 路径、键排序紧凑 JSON)", "type": "string", "pattern": "^[a-f0-9]{64}$" }
|
||
}
|
||
}
|