Skip to content

Latest commit

 

History

History
387 lines (347 loc) · 16.5 KB

File metadata and controls

387 lines (347 loc) · 16.5 KB

Baota 11.7.0 static staging contract

Status: exact-version, fail-closed contract. Retrieved official sources: 2026-07-25.

This document is the operator-facing contract for the one live Baota mutation reserved by SiteOps. It stages a validated ZIP archive in a new, release-owned directory for a non-production static site. It does not change the site's traffic, document root, runtime, database, or promotion pointer.

The only evidenced live version is exactly 11.7.0. This contract does not claim support for other 11.7.x versions. The site.static.stage capability stays unavailable until the exact endpoint, action, and response-schema preflight described here succeeds against the selected target.

Official facts

The following facts come from the linked Baota API documentation. The source documents define the HTTP method, action names, and fields. SiteOps adds the validation, containment, capability, and evidence rules in the next section.

Official API sources

Source Retrieved Use in this contract
Baota API 2026-07-25 API documentation index
getData 2026-07-25 Read the site inventory
CreateFile 2026-07-25 Create the empty staging child
UploadFile 2026-07-25 Upload the validated ZIP
mutil_unzip 2026-07-25 Extract the uploaded ZIP
GetDir 2026-07-25 Read the staged file tree
test_path 2026-07-25 Check a staged path

Allowlisted calls

Only the six calls in this table are allowed. A client must not expose a generic action, endpoint, file-manager, or command request. The fieldNames column lists the action-specific fields for that call. Every authenticated request also carries the global transport auth fields request_time and request_token declared in authFieldNames below.

Exact call Encoding Required action-specific fields
POST /data action=getData table=sites type=-1 form action=getData, table=sites, type=-1
POST /files action=CreateDir path form action=CreateDir, path
POST /files action=UploadFile path zunfile request_time request_token multipart action=UploadFile, path, zunfile
POST /files action=mutil_unzip sfile_list dfile coding=utf-8 type1=zip form action=mutil_unzip, sfile_list, dfile, coding=utf-8, type1=zip
POST /files action=GetDir path form action=GetDir, path
POST /files action=test_path path form action=test_path, path

The documented mutil_unzip response shape is an object containing status, msg, success, and fail. A target's observed response schema is recorded during preflight. SiteOps does not substitute a different schema or infer completion from one boolean field.

SiteOps policy

These rules are SiteOps policy, not claims about Baota's sandbox or guarantees.

Site and authentication

  1. The client sends POST /data action=getData table=sites type=-1 and matches the requested site by exact data[].name equality. Zero matches fail. More than one match fails. Partial, case-insensitive, or substring matches are not accepted.
  2. For every authenticated request, request_time is the current Unix time in seconds. request_token is exactly MD5(request_time + MD5(api_sk)). Both values are fresh per request. These transport auth fields are recorded as the ordered global list authFieldNames and are added to every authenticated request, including the multipart UploadFile call. The fieldNames of each allowlisted call list only the action-specific fields; request_time and request_token are never duplicated in per-call fieldNames.
  3. This contract makes no claim of TTL or replay protection. HTTPS with normal certificate validation and IP allowlisting are mandatory SiteOps policy.
  4. The credential is referenced by a local credential reference. The API key and derived token are never written to a receipt or evidence record.

Archive limits

The input is a ZIP archive only. Validation happens before any remote mutation.

Limit or rule SiteOps value
Archive type ZIP only
Compressed size <= 64 MiB
Uncompressed size <= 256 MiB
Regular file count <= 5,000
Individual regular file size <= 32 MiB
Normalized path encoding UTF-8 POSIX relative path
Normalized path length <= 240 bytes

Reject absolute paths, .. path components, backslashes, NUL bytes, duplicate normalized names, duplicate case-folded normalized names, symlinks, hardlinks, devices, FIFOs, and sockets. Directories are allowed only as archive structure, not as additional file types. The archive is never treated as a source of remote paths.

Remote path ownership and containment

The operator configures one absolute, dedicated stagingRoot. SiteOps derives one child directory from the release UUID and artifact digest. The caller does not supply a remote path. Every normalized request path must be strictly beneath stagingRoot. The root and derived child must not be the production document root, and the child must be absent before CreateDir is sent.

This is client-side containment, not a Baota sandbox. It cannot defend a compromised panel or root account, and it cannot defend concurrent out-of-band mutation of the configured root or child.

Asynchronous completion and postconditions

The official Baota 11.7.0 documentation does not document an asynchronous extraction threshold. Current 11.8 source code observed outside the 11.7.0 documentation set queues extraction at >= 500 MiB. That 11.8 observation is corroborative only; this contract does not claim the 500 MiB threshold is an official 11.7.0 behavior. Corroborative only.

SiteOps's compressed limit is <= 64 MiB, which is below the observed 11.8 queueing threshold. The SiteOps slice therefore does not depend on asynchronous queueing, and SiteOps does not label below-threshold work as asynchronous. status:true from mutil_unzip does not prove that extraction has completed and does not provide atomic, durable, retryable, or idempotent completion semantics.

After each mutation, the client performs bounded read-after-write checks with the allowlisted GetDir and test_path calls. The receipt records the observed mutil_unzip response schema baseline and the postcondition observations. A timeout, connection reset, or malformed mutation response is unknown_outcome; there is no automatic retry.

Capability grant and unavailable reasons

site.static.stage is granted only when all of these facts are true:

Preflight fact Required value
Panel version exact 11.7.0
Endpoint, action, and response schema exact observed preflight for all six allowlisted calls
Staging root configured absolute dedicated stagingRoot
Transport HTTPS with normal certificate validation
Credential valid local credential reference
Site exact non-production static site
Runtime static, not Node, PHP, Docker, or another runtime

Any other version, partial endpoint set, missing or malformed response, missing root, missing credential reference, failed HTTPS policy, production site, or unsupported runtime is unavailable. The stable reasons used by the verifier and by capability evidence are version_missing, unsupported_version, staging_root_missing, and runtime_unsupported for the built-in negative fixtures. Other failed checks use the corresponding stable reason named by the failed preflight field.

Mutation and recovery boundary

The one live mutation is writing the validated ZIP into the newly created staging child and extracting it there. No operation in this contract provides idempotency, remote checksum verification, atomicity, overwrite, delete, promotion, rollback, runtime deployment, database migration, a generic file-manager, shell execution, or automatic AI execution. The ordered list excludedOperations in the machine contract enumerates these forbidden operations in their canonical order, and the verifier enforces that exact order.

Receipt and evidence

The receipt outcome is exactly staged only; traffic unchanged. It is emitted only after the bounded postconditions are recorded. A blocked preflight or an unknown_outcome never receives this success wording.

Every receipt and evidence record includes these fields:

Field Required value
targetDigest digest of the selected target identity
siteDigest digest of the exact matched site record
releaseDigest digest of the release identity
artifactDigest digest of the validated archive/artifact
capabilityGeneration generation used by the authorization decision
capabilityDigest digest of the exact capability snapshot
normalizedStagingChild derived child, after local normalization
endpointSequence ordered six-call sequence actually attempted
responseSchemaDigests digest for each observed response schema
postconditionObservations bounded GetDir and test_path observations
timestamps preflight, request, mutation, and observation times
outcome staged, blocked, or unknown_outcome
redactedErrorCode stable error code without credentials or tokens

Machine contract

The verifier parses the JSON block below. It checks identifiers, tables, limits, source links, and policy values. It does not snapshot or compare this document's prose.

{
  "contract": "baota-static-stage",
  "contractVersion": "1",
  "supportedVersion": "11.7.0",
  "versionPolicy": "exact",
  "capability": "site.static.stage",
  "officialSources": [
    "https://docs.bt.cn/api/",
    "https://docs.bt.cn/api/data/getData",
    "https://docs.bt.cn/api/files/CreateFile",
    "https://docs.bt.cn/api/files/UploadFile",
    "https://docs.bt.cn/api/files/mutil_unzip",
    "https://docs.bt.cn/api/files/GetDir",
    "https://docs.bt.cn/api/files/test_path"
  ],
  "retrieved": "2026-07-25",
  "allowlistedCalls": [
    {
      "method": "POST",
      "path": "/data",
      "action": "getData",
      "encoding": "form",
      "fieldNames": ["action", "table", "type"],
      "fixedFields": { "table": "sites", "type": -1 }
    },
    {
      "method": "POST",
      "path": "/files",
      "action": "CreateDir",
      "encoding": "form",
      "fieldNames": ["action", "path"]
    },
    {
      "method": "POST",
      "path": "/files",
      "action": "UploadFile",
      "encoding": "multipart",
      "fieldNames": ["action", "path", "zunfile"]
    },
    {
      "method": "POST",
      "path": "/files",
      "action": "mutil_unzip",
      "encoding": "form",
      "fieldNames": ["action", "sfile_list", "dfile", "coding", "type1"],
      "fixedFields": { "coding": "utf-8", "type1": "zip" }
    },
    {
      "method": "POST",
      "path": "/files",
      "action": "GetDir",
      "encoding": "form",
      "fieldNames": ["action", "path"]
    },
    {
      "method": "POST",
      "path": "/files",
      "action": "test_path",
      "encoding": "form",
      "fieldNames": ["action", "path"]
    }
  ],
  "siteMatch": {
    "field": "data[].name",
    "comparison": "exact",
    "zeroMatches": "fail",
    "multipleMatches": "fail"
  },
  "auth": {
    "requestTime": "Unix seconds",
    "requestToken": "MD5(request_time + MD5(api_sk))",
    "freshPerRequest": true,
    "ttlOrReplayProtectionClaim": false,
    "https": "normal certificate validation",
    "ipAllowlisting": true
  },
  "authFieldNames": ["request_time", "request_token"],
  "archive": {
    "type": "ZIP only",
    "compressedMiBMax": 64,
    "uncompressedMiBMax": 256,
    "regularFileCountMax": 5000,
    "regularFileMiBMax": 32,
    "pathEncoding": "UTF-8",
    "pathStyle": "POSIX relative",
    "pathBytesMax": 240,
    "forbidden": [
      "absolute paths",
      "..",
      "backslashes",
      "NUL",
      "duplicate normalized names",
      "duplicate normalized case-folded names",
      "symlinks",
      "hardlinks",
      "devices",
      "FIFOs",
      "sockets"
    ]
  },
  "remotePaths": {
    "rootName": "stagingRoot",
    "root": "absolute dedicated operator configuration",
    "childDerivedFrom": ["release UUID", "artifact digest"],
    "callerSuppliedRemotePath": false,
    "normalizedRequestPath": "strictly beneath stagingRoot",
    "productionDocumentRoot": false,
    "childAbsentBeforeCreation": true,
    "containment": "client-side containment, not a Baota sandbox",
    "outOfBandMutationDefense": false
  },
  "completion": {
    "corroborativeAsynchronousThresholdMiB": 500,
    "corroborativeAsynchronousThresholdSource": "11.8 source",
    "statusTrueMeansComplete": false,
    "postconditions": ["GetDir", "test_path"],
    "observedMutilUnzipSchemaRequired": true,
    "officialMutilUnzipSchema": ["status", "msg", "success", "fail"]
  },
  "capabilityPreflight": {
    "requiredVersion": "11.7.0",
    "requiredEndpointActionSchema": "exact observed preflight",
    "requiredStagingRoot": true,
    "requiredHttps": true,
    "requiredCredentialReference": true,
    "requiredNonProductionSite": true,
    "requiredRuntime": "static",
    "grant": "site.static.stage",
    "partialOrMalformedUnavailable": true
  },
  "negativeFixtures": [
    {
      "name": "missing_version",
      "reason": "version_missing",
      "capability": null
    },
    {
      "name": "version_99_0",
      "reason": "unsupported_version",
      "capability": null
    },
    {
      "name": "missing_staging_root",
      "reason": "staging_root_missing",
      "capability": null
    },
    {
      "name": "node_runtime",
      "reason": "runtime_unsupported",
      "capability": null
    }
  ],
  "unknownOutcome": {
    "inputs": ["timeout", "connection reset", "malformed mutation response"],
    "result": "unknown_outcome",
    "automaticRetry": false
  },
  "excludedOperations": [
    "idempotency",
    "checksum",
    "atomicity",
    "overwrite",
    "delete",
    "promotion",
    "rollback",
    "runtime deploy",
    "DB migration",
    "generic file-manager",
    "shell",
    "automatic AI execution"
  ],
  "receipt": {
    "wording": "staged only; traffic unchanged",
    "requiredFields": [
      "targetDigest",
      "siteDigest",
      "releaseDigest",
      "artifactDigest",
      "capabilityGeneration",
      "capabilityDigest",
      "normalizedStagingChild",
      "endpointSequence",
      "responseSchemaDigests",
      "postconditionObservations",
      "timestamps",
      "outcome",
      "redactedErrorCode"
    ]
  }
}