API 使用手冊API manual

jt-ipam 的 REST API、phpIPAM 相容層、Graylog 查表與 MCP。以下所有網址請把 https://ipam.example.com 換成你自己的位址。 The jt-ipam REST API, phpIPAM compatibility layer, Graylog lookups and MCP. Replace https://ipam.example.com below with your own address.

API 一律走 HTTPS。jt-ipam 強制 HTTPS,明文 HTTP 不會提供服務。若你的 jt-ipam 用自簽憑證,curl 要加 -k(或把 CA 加進信任庫)。 The API is HTTPS-only. jt-ipam enforces HTTPS and does not serve plain HTTP. If your instance uses a self-signed certificate, add -k to curl (or add the CA to your trust store).

1認證:API 權杖Auth: API tokens

外部程式一律用 API 權杖,不要用帳號密碼登入拿 JWT(JWT 只有 15 分鐘、是給網頁前端用的)。 External programs should always use an API token rather than logging in for a JWT (JWTs last 15 minutes and exist for the web UI).

怎麼取得權杖Getting a token

登入 jt-ipam → 右上角使用者選單 → API 權杖 → 新增。填名稱、有效天數(1~365,預設 90)、權限範圍,按儲存。 Sign in to jt-ipam → user menu (top right) → API tokens → Add. Give it a name, a lifetime (1–365 days, default 90) and a scope, then save.

明文只出現一次。建立完成的那個視窗是你唯一能看到完整權杖的機會 —— 資料庫只存 SHA-256 雜湊。沒抄到就撤銷重建,沒有別的辦法找回來。 The plaintext is shown once. The dialog right after creation is your only chance to see the full token — only its SHA-256 hash is stored. If you lose it, revoke and create a new one; there is no way to recover it.

怎麼帶權杖Sending the token

curl -H "Authorization: Bearer jt_prod_xxxxxxxxxxxxxxxx" \
  https://ipam.example.com/api/v1/subnets

權杖首碼 jt_ 會讓後端自動辨識為 API 權杖(沒有這個首碼則當成 JWT 解析)。 The jt_ prefix tells the backend this is an API token (without it, the value is parsed as a JWT).

權限範圍(scope)Scopes

scopes 效果Effect
[] 不限制 —— 沿用權杖擁有者的權限,可讀可寫。 Unrestricted — inherits the owner's permissions, read and write.
["read"] 唯讀 —— GETHEADOPTIONS 可用;POSTPATCHPUTDELETE 一律回 403 Read-only — GET/HEAD/OPTIONS work; POST/PATCH/PUT/DELETE always return 403.

目前只支援這兩種。填其他值(例如 writesubnets:read)建立時就會被拒絕回 422 —— 這是刻意的:不提供無法真正強制執行的權限旋鈕。 These are the only two supported. Any other value (e.g. write, subnets:read) is rejected with 422 at creation — deliberately, so there is no knob that looks like a restriction but isn't enforced.

權杖繼承擁有者權限。用 admin 帳號建的權杖就是一把 admin 全權金鑰。要給第三方受限的存取,正確做法是:另建一個低權限使用者 → 在「管理 → 權限」把需要的物件(區段/子網路/裝置…)授權給它 → 用那個帳號登入建立權杖。object_filters 欄位目前保留但不生效,別依賴它。 A token inherits its owner's permissions. A token created by an admin is a full-privilege admin key. To give a third party restricted access: create a low-privilege user, grant it the objects it needs (sections / subnets / devices…) under Admin → Permissions, then sign in as that user to create the token. The object_filters field is reserved but not enforced — do not rely on it.

用 API 管理權杖Managing tokens via the API

方法Method路徑Path說明Description
GET/api/v1/api-tokens 列出自己的權杖(admin 可加 ?user_id= 看別人的)List your own tokens (admins may add ?user_id=)
POST/api/v1/api-tokens 建立,回應含一次性明文Create; the response carries the one-time plaintext
DELETE/api/v1/api-tokens/{id} 撤銷(立即失效,不可復原)Revoke (immediate, irreversible)
curl -X POST https://ipam.example.com/api/v1/api-tokens \
  -H "Authorization: Bearer <jwt-or-token>" \
  -H "Content-Type: application/json" \
  -d '{"name":"monitoring","expires_in_days":180,"scopes":["read"]}'
{
  "id": "3f1c...", "name": "monitoring",
  "token": "jt_prod_XXXXXXXXXXXXXXXXXXXX",
  "token_prefix": "jt_prod_", "expires_at": "2027-01-26T00:00:00Z",
  "scopes": ["read"]
}

2通用慣例Conventions

  • 基底路徑/api/v1。所有請求與回應都是 JSON(Content-Type: application/json),除少數明講的 CSV 端點。 Base path: /api/v1. Requests and responses are JSON (Content-Type: application/json), apart from a few explicitly-CSV endpoints.
  • 識別碼是 UUID,不是遞增整數。 Identifiers are UUIDs, not incrementing integers.
  • 時間一律 ISO 8601、UTC(2026-07-30T12:34:56Z)。 Timestamps are ISO 8601 in UTC (2026-07-30T12:34:56Z).
  • 未知欄位會被拒絕。寫入用的 schema 是嚴格模式(extra=forbid)—— 多送一個拼錯的欄位會回 422,而不是安靜忽略。 Unknown fields are rejected. Write schemas are strict (extra=forbid) — a misspelled extra field returns 422 instead of being silently ignored.
  • 部分更新用 PATCH,只送要改的欄位。 Use PATCH for partial updates, sending only the fields you want to change.

分頁Pagination

列表端點吃 page(從 1 起算)與 page_size,回應固定是這個外層: List endpoints take page (1-based) and page_size, and always return this envelope:

{ "items": [ … ], "total": 1234, "page": 1, "page_size": 50 }

page_size 上限視端點而定(多數是 200)。要抓完整資料就依 total 逐頁取。 The page_size cap depends on the endpoint (usually 200). To fetch everything, page through using total.

3錯誤與狀態碼Errors & status codes

錯誤回應一律是 {"detail": "…"}422 另外附 errors 陣列指出哪個欄位不合法。伺服器端不會回 stack trace。 Errors are always {"detail": "…"}; 422 additionally carries an errors array pinpointing the invalid fields. Stack traces are never returned.

{ "detail": "Invalid request",
  "errors": [ { "loc": ["body","cidr"], "msg": "value is not a valid IPv4 network", "type": "value_error" } ] }
狀態Status意義與常見原因Meaning & common cause
200 / 201 / 204成功(204 用於刪除,無內容)Success (204 for deletes, no body)
401沒帶權杖、權杖無效、已撤銷或已到期Missing, invalid, revoked or expired token
403認證過但不准 —— RBAC 不足,或是唯讀權杖試圖寫入Authenticated but not allowed — insufficient RBAC, or a read-only token attempting a write
404不存在 —— 也用於「存在但你無權看見」,以免洩漏存在性Not found — also returned for "exists but you may not see it", to avoid leaking existence
409衝突,例如重疊網段、或刪除仍被引用的物件Conflict, e.g. overlapping subnets, or deleting a still-referenced object
422欄位驗證失敗(含未知欄位、不支援的 scope)Validation failed (including unknown fields and unsupported scopes)
429超過速率限制,回應帶 Retry-AfterRate limited; the response carries Retry-After

4權限模型Permission model

jt-ipam 是預設關閉(deny by default)。你的權杖看得到什麼,完全取決於擁有者被授權了什麼: jt-ipam is deny by default. What your token can see depends entirely on what its owner has been granted:

  • 可逐物件授權的資料(區段/子網路/IP/裝置/機櫃/地點/客戶)→ 只回你可見範圍內的,total、統計與趨勢都會跟著縮放 Per-object data (sections / subnets / IPs / devices / racks / locations / customers) → only what you can see, and total, counts and trends scale with it.
  • 全域基礎設施資料(VLAN/VRF/NAT/防火牆/DNS/虛擬化/佈線/電力…)→ 需要「全域讀取」(admin 或萬用讀取授權)。只被指派特定物件的帳號會拿到 403 Global infrastructure (VLAN / VRF / NAT / firewalls / DNS / virtualization / cabling / power…) → requires global read (admin or a wildcard read grant). Accounts scoped to specific objects get 403.
  • 純管理資料(稽核記錄、使用者/群組/權限、系統與整合設定)→ 需要 admin。 Administrative data (audit log, users / groups / permissions, system and integration settings) → requires admin.

想知道自己這把權杖有什麼能力,打 GET /api/v1/me:回應含 is_adminhas_visibilityhas_global_read To discover what your token can do, call GET /api/v1/me: the response includes is_admin, has_visibility and has_global_read.

5區段(Sections)Sections

最上層的分組容器,子網路掛在區段底下。可有父子階層。 The top-level grouping container that subnets belong to. Supports parent/child nesting.

方法Method路徑Path說明Description
GET/api/v1/sections列出(分頁)List (paginated)
GET/api/v1/sections/{id}單筆詳情Get one
POST/api/v1/sections建立Create
PATCH/api/v1/sections/{id}部分更新Partial update
DELETE/api/v1/sections/{id}刪除Delete
POST/api/v1/sections/bulk-delete批次刪除({"ids":[…]}Bulk delete ({"ids":[…]})

建立區段Create a section

欄位Field型別Type必填Required說明Description
namestringyes1~128 字1–128 chars
descriptionstring最多 1024 字up to 1024 chars
parent_iduuid上層區段Parent section
customer_iduuid歸屬客戶/管理單位Owning customer / managing unit
strict_modebool嚴格模式Strict mode
display_orderint顯示排序(0~10000)Display order (0–10000)
curl -X POST https://ipam.example.com/api/v1/sections \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"name":"Taipei DC","description":"main site"}'

6子網路(Subnets)Subnets

方法Method路徑Path說明Description
GET/api/v1/subnets列出。可加 section_idarchivedpagepage_sizeList. Accepts section_id, archived, page, page_size
GET/api/v1/subnets/{id}單筆詳情Get one
GET/api/v1/subnets/{id}/usage使用率統計(已用/可用/可配發)Usage stats (used / free / usable)
GET/api/v1/subnets/{id}/first_free_address第一個可用 IPFirst free IP
POST/api/v1/subnets建立Create
PATCH/api/v1/subnets/{id}部分更新Partial update
POST/api/v1/subnets/{id}/archive封存 / /unarchive 取消封存Archive / /unarchive to restore
POST/api/v1/subnets/{id}/scan要求掃描代理立刻掃一次Ask the scan agent to sweep it now
DELETE/api/v1/subnets/{id}刪除(底下仍有 IP/下層時會被擋)Delete (blocked while it still holds IPs or children)
GET/api/v1/subnets/overlaps/exists是否存在重疊網段(admin)Whether any overlapping subnets exist (admin)
使用率請用 /usage,不要自己算 CIDR。jt-ipam 的「已用/可用」是依實際 IP 記錄與網段設定算的,跟純數學的 CIDR 容量不一樣(會扣掉網路位址、廣播位址、保留與 DHCP 範圍等)。 Use /usage rather than computing from the CIDR. jt-ipam's used/free figures come from the actual IP records and subnet settings, which differ from raw CIDR arithmetic (network and broadcast addresses, reservations, DHCP pools and so on).
curl -X POST https://ipam.example.com/api/v1/subnets \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"cidr":"192.0.2.0/24","section_id":"<section-uuid>","description":"office LAN"}'

7IP 位址(Addresses)IP addresses

方法Method路徑Path說明Description
GET/api/v1/addresses列出/查詢(見下方參數)List / query (parameters below)
GET/api/v1/addresses/{id}單筆詳情Get one
GET/api/v1/addresses/{id}/history異動記錄Change history
GET/api/v1/addresses/{id}/relations關聯物件Related objects
GET/api/v1/addresses/{id}/switch-port對應的交換器連接埠The switch port it is seen on
GET/api/v1/addresses/export.csv匯出 CSVExport as CSV
POST/api/v1/addresses建立Create
POST/api/v1/addresses/first_free取得並配發第一個可用 IPClaim the first free IP
PATCH/api/v1/addresses/{id}部分更新Partial update
DELETE/api/v1/addresses/{id}刪除Delete
POST/api/v1/addresses/bulk-state批次改狀態Bulk state change
POST/api/v1/addresses/import批次匯入Bulk import

查詢參數Query parameters

參數Parameter說明Description
subnet_id限定某個子網路Restrict to one subnet
section_id限定某個區段Restrict to one section
customer_id限定某個客戶/管理單位Restrict to one customer
device_id限定掛在某個裝置上Restrict to one device
q關鍵字(IP、主機名稱、MAC、說明…)Free text (IP, hostname, MAC, description…)
exact精準比對而非模糊Exact match instead of fuzzy
sort / order排序欄位 / ascdescSort field / ascdesc
# 查某網段裡所有 IP
curl -G https://ipam.example.com/api/v1/addresses \
  -H "Authorization: Bearer $TOKEN" \
  --data-urlencode "subnet_id=<subnet-uuid>" --data-urlencode "page_size=200"

# 依主機名稱找
curl -G https://ipam.example.com/api/v1/addresses \
  -H "Authorization: Bearer $TOKEN" --data-urlencode "q=web01"
同一個 IP 字串可能出現多筆。唯一鍵是「子網路 + IP」,所以在重疊網段(不同客戶共用 192.168.1.0/24)的環境裡,同一個 IP 會跨多個子網路各有一筆 —— 這是設計如此。用 subnet_id 限定範圍,不要假設 IP 唯一。 The same IP string may appear more than once. The unique key is (subnet, IP), so with overlapping subnets (different customers sharing 192.168.1.0/24) one IP legitimately has a row per subnet. Scope your queries with subnet_id; don't assume an IP is unique.

8裝置(Devices)Devices

方法Method路徑Path說明Description
GET/api/v1/devices列出List
GET/api/v1/devices/{id}單筆詳情Get one
GET/api/v1/devices/{id}/relations關聯(IP/機櫃/佈線…)Relations (IPs / rack / cabling…)
GET/api/v1/devices/{id}/integrations各整合帶回的資料Data pulled in by integrations
GET/api/v1/devices/{id}/vlansVLAN 對應VLAN mappings
POST/api/v1/devices建立Create
PATCH/api/v1/devices/{id}部分更新Partial update
DELETE/api/v1/devices/{id}刪除Delete

GET /api/v1/search?q=…&limit_per_type=… 是全域搜尋,跨區段/子網路/IP/裝置/客戶等型別,並依你的可見範圍過濾。支援部分 MAC 首碼(如 bc:24)。 GET /api/v1/search?q=…&limit_per_type=… searches across sections, subnets, IPs, devices, customers and more, filtered to what you may see. Partial MAC prefixes (e.g. bc:24) work.

/api/v1/tools/* 是一組無副作用的計算端點,適合腳本直接呼叫: /api/v1/tools/* is a set of side-effect-free calculators handy for scripts:

端點Endpoint用途Purpose
ip-info / cidr-infoIP/網段基本資訊IP / CIDR facts
cidr-split / aggregate切分 / 聚合網段Split / aggregate prefixes
ip-in-cidr / cidr-relation包含關係判斷Containment and relation checks
range-to-cidr / cidr-to-range範圍 ↔ 網段互轉Range ↔ CIDR conversion
netmask / eui64 / mac-format遮罩換算、EUI-64、MAC 格式化Netmask conversion, EUI-64, MAC formatting
dns-lookup / fqdn / dns-mail / geoipDNS 查詢、FQDN 解析、郵件記錄、GeoIPDNS lookups, FQDN parsing, mail records, GeoIP

10完整資源索引Full resource index

API 共約 500 條路由。上面詳述的是最常用的核心資源;其餘資源大多遵循同一套 GET 列表 / GET {id}POSTPATCHDELETE 慣例。 The API has roughly 500 routes. The sections above cover the core resources in detail; most others follow the same GET list / GET {id} / POST / PATCH / DELETE convention.

分類Area基底路徑Base paths權限Access
核心 IPAMCore IPAM /sections /subnets /addresses /devices /customers /racks /locations 逐物件可見性Per-object visibility
網路資源Network resources /vlans /vlan-domains /vrfs /asns /nat /dhcp-ranges /vpn-tunnels /wireless 全域讀取Global read
實體層Physical layer /cables /cable-terminations /device-ports /ports /power-feeds /power-panels /power-outlets /device-power-ports 全域讀取Global read
商務/組織Business / org /tenants /tenant-groups /providers /circuits /circuit-types /contacts /contact-groups /contact-assignments 全域讀取Global read
整合Integrations /firewalls(OPNsense)/pfsense /fortigate /windows-dhcp /librenms /wazuh /adguard /dns /virt 設定需 admin;唯讀檢視需全域讀取admin to configure; global read to view
代理與憑證Agents & certificates /scan-agents /cert-agents /certificates /ssh-credentials admin
流程與通知Workflow & notifications /ip-requests /notifications /webhooks /tasks /anomalies /ip-changes 視端點而定Varies
分析與 AIAnalytics & AI /dashboard /topology /search /tools /oui /ai 視端點而定Varies
管理Administration /users /groups /audit /system /custom-fields /plugins /import /migration admin
自助Self-service /me /me/preferences /api-tokens /auth 任何已認證帳號Any authenticated account

11phpIPAM 相容 API-compatible API

給既有的 phpIPAM 腳本用的相容層:換掉網址與權杖就能沿用,回應是 phpIPAM 的外層格式。 A compatibility layer for existing phpIPAM scripts: change the URL and token and they keep working, with phpIPAM-shaped response envelopes.

基底路徑/api/phpipam/<app_id>/<app_id> 沿用你原本 phpIPAM 的 App ID 字串。 Base path: /api/phpipam/<app_id>/, where <app_id> is whatever App ID string your scripts already use.

認證Authentication

三種標頭都收(挑一個就好):token: jt_…phpipam-token: jt_…Authorization: Bearer jt_…。也可以用帳號密碼走 Basic Auth 換一把 30 天的權杖: Any one of three headers works: token: jt_…, phpipam-token: jt_…, or Authorization: Bearer jt_…. You can also exchange username/password over Basic Auth for a 30-day token:

curl -X POST -u 'user:password' \
  https://ipam.example.com/api/phpipam/myapp/user/
{ "code":200, "success":true, "data":{ "token":"jt_prod_…", "expires":"…" }, "time":0.01 }
方法Method路徑(接在基底之後)Path (after the base)說明Description
POSTuser/Basic Auth 換權杖Exchange Basic Auth for a token
GETuser/權杖資訊Token info
PATCHuser/延長權杖期限Extend the token
DELETEuser/撤銷權杖(登出)Revoke the token (log out)
GETsections/ · sections/{id} · sections/{id}/subnets/區段Sections
GETsubnets/{id}/ · subnets/cidr/{cidr}/ · subnets/{id}/usage/ · subnets/{id}/first_free/ · subnets/{id}/addresses/子網路Subnets
GETaddresses/{id}/ · addresses/{ip}/{subnet_id}/ · addresses/search/{ip}/ · addresses/search_hostname/{host}/ · addresses/first_free/{subnet_id}/IP 位址IP addresses
POST PATCH DELETE sections/ · subnets/ · addresses/建立/更新/刪除Create / update / delete
相容層只覆蓋 phpIPAM API 的常用子集,不是逐一對應。完整對照見原始碼裡的 docs/PHPIPAM_API_MAPPING.md。新寫的整合建議直接用上面的原生 REST API。 The layer covers the commonly-used subset of the phpIPAM API, not every call. See docs/PHPIPAM_API_MAPPING.md in the source for the full mapping. For new integrations, prefer the native REST API above.
唯讀權杖在這一層同樣有效:POSTPATCH 會被擋成 403。唯一例外是 DELETE user/(撤銷自己的權杖)—— 那是降權、不是異動資料,所以照樣放行,老腳本的「登入 → 查詢 → 登出」流程不會斷。 Read-only tokens are enforced here too: POST/PATCH get 403. The one exception is DELETE user/ (revoking your own token) — that reduces privilege rather than changing data, so it is allowed and the classic login → query → logout flow keeps working.

12Graylog DSV 查表 lookups

給 Graylog 的「DSV File from HTTP」配接器用的免登入唯讀端點,回 CSV。認證是一把系統層級的權杖,走網址參數 ?token=(由 admin 在「管理 → Graylog DSV」產生),因為 Graylog 抓檔時不能帶自訂標頭。 Unauthenticated, read-only CSV endpoints for Graylog's "DSV File from HTTP" adapter. They are gated by a system-level token passed as ?token= (generated under Admin → Graylog DSV), because Graylog cannot send custom headers when fetching.

路徑Path對照Mapping
/api/v1/lookup/{name}IP → 主機名稱IP → hostname
/api/v1/lookup/firewall/{id}/rule-aliasesOPNsense 規則 rid → 別名OPNsense rule rid → alias
/api/v1/lookup/firewall/{id}/aliasesOPNsense 別名 → 成員OPNsense alias → members
/api/v1/lookup/pfsense/{id}/rulespfSense tracker → 說明pfSense tracker → description
/api/v1/lookup/pfsense/{id}/aliasespfSense 別名 → 成員pfSense alias → members
/api/v1/lookup/proxmox/vms全部叢集:vmid → VM 名稱All clusters: vmid → VM name
/api/v1/lookup/proxmox/{cluster_id}/vms單一叢集:vmid → VM 名稱One cluster: vmid → VM name
curl "https://ipam.example.com/api/v1/lookup/hostnames?token=<dsv-token>"
"192.0.2.10","web01"
"192.0.2.11","db01"

在 Graylog 的 DSV 配接器裡,Key column = 0、Value column = 1(欄位索引從 0 起算)。防火牆相關端點要先在該台整合設定裡勾選「提供防火牆 DSV」才會有資料。 In Graylog's DSV adapter set Key column = 0 and Value column = 1 (indices are 0-based). The firewall endpoints only return data once "expose firewall DSV" is enabled on that instance.

13MCP(給 LLM 用) (for LLMs)

jt-ipam 內建 MCP(Model Context Protocol)伺服器,讓 LLM 用自然語言查 IPAM。走 JSON-RPC 2.0: jt-ipam ships an MCP (Model Context Protocol) server so an LLM can query the IPAM in natural language, over JSON-RPC 2.0:

  • Streamable HTTPPOST /api/mcpinitialize 的回應會帶 Mcp-Session-Id 標頭。支援 initializepingtools/listtools/call Streamable HTTP: POST /api/mcp. The initialize response carries an Mcp-Session-Id header. Supports initialize, ping, tools/list and tools/call.
  • stdio(本機用,例如 Claude Desktop):python -m app.mcp.stdio_server,權杖放環境變數 JT_IPAM_MCP_TOKEN stdio (local, e.g. Claude Desktop): python -m app.mcp.stdio_server, with the token in JT_IPAM_MCP_TOKEN.

認證X-Auth-TokenAuthorization: Bearer,可以是: Authentication uses X-Auth-Token or Authorization: Bearer, with either:

  • 對外唯讀 MCP 金鑰jtmcp_…,admin 在「管理 → LLM/AI」產生並輪替)—— 只看得到唯讀工具,異動類工具會被隱藏並擋下。要先啟用「對外 MCP」才會生效。 A read-only external MCP key (jtmcp_…, generated and rotated by an admin under Admin → LLM/AI) — read-only tools only; mutating tools are hidden and blocked. External MCP must be enabled first.
  • 一般 API 權杖jt_…)—— 依該權杖擁有者的權限。若這把權杖是唯讀 scope,MCP 同樣只給唯讀工具。 A regular API token (jt_…) — scoped to its owner's permissions. If the token is read-only, MCP likewise exposes only read-only tools.
curl -X POST https://ipam.example.com/api/mcp \
  -H "X-Auth-Token: jtmcp_xxxxxxxx" -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

14代理協定Agent protocols

掃描代理與憑證派送代理有自己的通道,用各自獨立的金鑰X-Agent-Key 標頭,建立代理時產生、只存雜湊),不是 API 權杖。這些端點是給代理程式用的,一般整合不需要碰。 The scan agent and certificate-distribution agent have their own channel, authenticated by per-agent keys (the X-Agent-Key header, issued when the agent is created and stored only as a hash) rather than API tokens. These endpoints are for the agents themselves; normal integrations don't need them.

路徑Path用途Purpose
GET /api/v1/scan-agents/poll代理取得要掃的子網路與方法Agent fetches the subnets and probes to run
POST /api/v1/scan-agents/report代理回報存活 IP、MAC、主機名稱Agent reports live IPs, MACs and hostnames
GET /api/v1/cert-agents/check代理比對指紋與到期日Agent compares fingerprints and expiry
GET /api/v1/cert-agents/bundle/raw代理取憑證內容(多種格式)Agent fetches certificate material (several formats)
POST /api/v1/cert-agents/report代理回報派送結果Agent reports deployment results
GET /api/v1/cert-agents/agent.sh
GET /api/v1/cert-agents/agent.ps1
代理程式本體(Linux / Windows),公開可取、無密鑰;代理據此自我更新The agent itself (Linux / Windows). Public, contains no secrets; agents self-update from it

bundle/raw?part=pkcs12 可帶 X-Pfx-Password 標頭。帶了就回以該密碼加密、且 Windows 匯得進去的 PKCS#12(PBESv1-SHA1-3DES)—— PBESv1 是 Windows CryptoAPI 全版本都吃的形式;函式庫預設的 PBESv2/AES-256 失敗時訊息是誤導的「密碼不正確」。不帶標頭時行為不變(不加密)。Windows 代理每次執行自產一組隨機密碼且只留在記憶體,私鑰不會為了匯入而寫到磁碟。 bundle/raw?part=pkcs12 accepts an X-Pfx-Password header. With it you get a PKCS#12 encrypted with that password using the encryption Windows can actually import (PBESv1-SHA1-3DES) — PBESv1 is the form every version of the Windows CryptoAPI accepts; the library default, PBESv2/AES-256, fails with a misleading "the password is incorrect". Without the header the behaviour is unchanged (unencrypted). The Windows agent generates a random password per run and keeps it in memory, so the private key is never written to disk on the way in.

憑證代理的取檔端點會即時解密並回傳私鑰(強制 HTTPS、逐次寫稽核)。那把 X-Agent-Key 等同於它 scope 內所有憑證的私鑰,請當成最高敏感憑證保管。 The certificate bundle endpoint decrypts and returns private keys (HTTPS-only, audited on every fetch). That X-Agent-Key is equivalent to the private keys of every certificate in its scope — treat it as a top-tier secret.

另有兩支完全免認證的健康檢查端點,給負載平衡器用:GET /healthz(存活)與 GET /readyz(就緒,DB/Redis 不通時回 503)。 Two entirely unauthenticated health endpoints exist for load balancers: GET /healthz (liveness) and GET /readyz (readiness, returning 503 when the DB or Redis is unreachable).

15速率限制與 CORSRate limits & CORS

依來源 IP 分桶限流,超過回 429 並帶 Retry-After(秒)。預設值(可在後端設定調整): Rate limits are bucketed per source IP; exceeding one returns 429 with Retry-After (seconds). Defaults (configurable in the backend):

分桶Bucket預設Default適用Applies to
api_token600 / min用 API 權杖呼叫API-token calls
default100 / min一般請求General requests
auth10 / min登入等認證端點Login and other auth endpoints
ai20 / minLLM 推論(昂貴,較嚴格)LLM inference (expensive, tighter)

CORS:正式環境只允許明確列出的 HTTPS 來源(不接受 *、不接受 HTTP)。因此瀏覽器端的跨網域呼叫預設不會通 —— 第三方整合請走伺服器對伺服器。 CORS: in production only explicitly-listed HTTPS origins are allowed (no *, no HTTP). So cross-origin browser calls will not work by default — do third-party integrations server-to-server.

16OpenAPI

後端是 FastAPI,會自動產生 OpenAPI 規格。但正式環境(APP_ENV=production)一律關閉 /docs/redoc/openapi.json,避免對外洩漏完整攻擊面。 The backend is FastAPI and generates an OpenAPI spec automatically. However /docs, /redoc and /openapi.json are disabled in production (APP_ENV=production) so the full attack surface isn't published.

要拿完整的機器可讀規格(含每個端點的全部欄位),在非正式環境的實例上開一台即可: To get the complete machine-readable spec (every field of every endpoint), run a non-production instance:

APP_ENV=development uvicorn app.main:app
curl http://127.0.0.1:8000/openapi.json > jt-ipam-openapi.json

拿到 openapi.json 之後可以直接餵給 openapi-generator 產出各語言的 client。 Feed that openapi.json to openapi-generator to produce a client in your language of choice.