# Batch Plan JSON Schema

This document defines the JSON format used by `alidns_batch.py` for batch DNS provisioning.

## Schema

```json
[
    {
        "rr":    "(required) string - Subdomain prefix. '@' for root domain.",
        "type":  "(required) string - DNS record type: A, AAAA, CNAME, MX, TXT, NS, SRV, CAA",
        "value": "(required) string - Record value (IP address, domain, text, etc.)",
        "ttl":   "(optional) integer - TTL in seconds. Default: 600",
        "scope": "(optional) string - 'public' or 'private'. Used for security validation.",
        "note":  "(optional) string - Human-readable description of the record's purpose."
    }
]
```

## Example: GoGenex.com Full Plan

Based on the domain planning document, here is a complete plan example:

```json
[
    {"rr": "@",            "type": "A",     "value": "<CDN_LB_IP>",           "ttl": 600,  "scope": "public",  "note": "Root domain - corporate website"},
    {"rr": "www",          "type": "CNAME", "value": "gogenex.com",           "ttl": 600,  "scope": "public",  "note": "WWW redirect to root"},
    {"rr": "portal",       "type": "A",     "value": "<PORTAL_LB_IP>",       "ttl": 600,  "scope": "public",  "note": "User business portal"},
    {"rr": "console",      "type": "A",     "value": "<CONSOLE_LB_IP>",      "ttl": 600,  "scope": "public",  "note": "Developer/merchant console"},
    {"rr": "admin",        "type": "A",     "value": "10.0.1.10",            "ttl": 600,  "scope": "private", "note": "Internal admin dashboard"},

    {"rr": "api",          "type": "A",     "value": "<API_GW_LB_IP>",       "ttl": 300,  "scope": "public",  "note": "Public API gateway"},
    {"rr": "api-internal", "type": "A",     "value": "10.0.2.10",            "ttl": 600,  "scope": "private", "note": "Internal microservice API gateway"},

    {"rr": "auth",         "type": "A",     "value": "<AUTH_IP>",            "ttl": 300,  "scope": "public",  "note": "SSO / OAuth2 authentication"},
    {"rr": "mpc",          "type": "A",     "value": "10.0.10.10",           "ttl": 600,  "scope": "private", "note": "MPC signing service (isolated)"},
    {"rr": "mpc-relay",    "type": "A",     "value": "10.0.10.11",           "ttl": 600,  "scope": "private", "note": "MPC relay/coordinator node"},
    {"rr": "mpc-callback", "type": "A",     "value": "10.0.10.12",           "ttl": 600,  "scope": "private", "note": "MPC async callback"},
    {"rr": "kms",          "type": "A",     "value": "10.0.10.20",           "ttl": 600,  "scope": "private", "note": "Key management service"},
    {"rr": "vault",        "type": "A",     "value": "10.0.10.21",           "ttl": 600,  "scope": "private", "note": "HashiCorp Vault secret storage"},

    {"rr": "rpc",          "type": "A",     "value": "<RPC_PROXY_LB_IP>",    "ttl": 300,  "scope": "public",  "note": "Blockchain RPC proxy"},
    {"rr": "ws",           "type": "A",     "value": "<WS_LB_IP>",           "ttl": 300,  "scope": "public",  "note": "WebSocket service"},
    {"rr": "explorer",     "type": "A",     "value": "<EXPLORER_IP>",        "ttl": 600,  "scope": "public",  "note": "Blockchain explorer"},
    {"rr": "indexer",      "type": "A",     "value": "10.0.3.10",            "ttl": 600,  "scope": "private", "note": "On-chain data indexer"},
    {"rr": "bridge",       "type": "A",     "value": "<BRIDGE_LB_IP>",       "ttl": 600,  "scope": "public",  "note": "Cross-chain bridge"},
    {"rr": "oracle",       "type": "A",     "value": "10.0.3.20",            "ttl": 600,  "scope": "private", "note": "Oracle service"},
    {"rr": "node",         "type": "A",     "value": "10.0.3.30",            "ttl": 600,  "scope": "private", "note": "Full node management"},
    {"rr": "faucet",       "type": "A",     "value": "<FAUCET_IP>",          "ttl": 600,  "scope": "public",  "note": "Testnet faucet"},

    {"rr": "oss",          "type": "A",     "value": "10.0.4.10",            "ttl": 600,  "scope": "private", "note": "MinIO object storage"},
    {"rr": "static",       "type": "CNAME", "value": "<CDN_DOMAIN>",         "ttl": 300,  "scope": "public",  "note": "Static assets CDN"},
    {"rr": "ipfs-gw",      "type": "A",     "value": "<IPFS_GW_IP>",        "ttl": 600,  "scope": "public",  "note": "IPFS gateway"},

    {"rr": "monitor",      "type": "A",     "value": "10.0.5.10",            "ttl": 600,  "scope": "private", "note": "Grafana monitoring"},
    {"rr": "log",          "type": "A",     "value": "10.0.5.11",            "ttl": 600,  "scope": "private", "note": "Log platform (ELK/Loki)"},
    {"rr": "trace",        "type": "A",     "value": "10.0.5.12",            "ttl": 600,  "scope": "private", "note": "Tracing (Jaeger/Tempo)"},
    {"rr": "alert",        "type": "A",     "value": "10.0.5.13",            "ttl": 600,  "scope": "private", "note": "Alertmanager"},
    {"rr": "status",       "type": "CNAME", "value": "<STATUS_PAGE_DOMAIN>", "ttl": 600,  "scope": "public",  "note": "Public status page"},
    {"rr": "ci",           "type": "A",     "value": "10.0.5.20",            "ttl": 600,  "scope": "private", "note": "CI/CD platform"},

    {"rr": "docs",         "type": "CNAME", "value": "<DOCS_HOSTING>",       "ttl": 600,  "scope": "public",  "note": "API documentation"},
    {"rr": "developer",    "type": "A",     "value": "<DEV_PLATFORM_IP>",    "ttl": 600,  "scope": "public",  "note": "Developer open platform"},
    {"rr": "sandbox",      "type": "A",     "value": "<SANDBOX_IP>",         "ttl": 600,  "scope": "public",  "note": "API sandbox environment"}
]
```

## Placeholder Convention

Values like `<CDN_LB_IP>` are placeholders. Replace them with actual IP addresses or domains before running the batch script. The script will reject entries with `<` or `>` in the value field.

## Generating a Plan from the Planning Document

To convert a markdown planning doc into a JSON plan:

1. Extract all subdomain entries from the tables
2. Determine record type (A for IPs, CNAME for domain aliases)
3. Set scope based on the "访问范围" column (公网 -> public, 内网 -> private)
4. Fill in actual IP addresses from your infrastructure
5. Save as `.json` and validate with `--dry-run` before applying
