~xdavidwu/cskloudv3-thesis

9df6e857bc258ddba58ba636846ccf1c2ce8b511 — Pinghao Wu 6 months ago b04e964
enrich refs
4 files changed, 102 insertions(+), 13 deletions(-)

M Sections/2.Backgrounds.tex
M Sections/4.Architecture.tex
M main.tex
M ref.bib
M Sections/2.Backgrounds.tex => Sections/2.Backgrounds.tex +4 -4
@@ 35,15 35,15 @@ Kubernetes resources 的種類形成了既定的語意和格式,例如:Pod 

Kubernetes 提供多個身份驗證手段,可搭配混合使用,在設計上盡量單獨以驗證手段提供完整身份資訊,包含身份名稱以及所在的群組等,叢集本身以不存放身份對應的資料為原則。這樣的設計使得伺服器負擔減少,同時具有架構單純、易於除錯等優勢,但根據手段不同,可能會使得更動身份資訊(如修改所在的群組)顯得不便。常見的驗證手段主要有:X.509 憑證、authenticating proxy、static tokens、webhook tokens、ServiceAccount tokens 與 OpenID Connect tokens。

Kubernetes 中的 X.509 憑證即為 TLS 客戶端憑證 (client certificate),在 TLS 的驗證模型下,除了伺服器端需要提供憑證證明本身的真實性以外,客戶端也可以提供憑證,使伺服器端得以驗證客戶端的身份。此機制近期又以 mutual TLS (mTLS) 的名稱流行,強調雙方都可以驗證彼此的身份。Kubernetes 將身份名稱與所在群組存放在客戶端憑證的 Subject 屬性內,並且要求憑證來自特定的簽發單位 (certificate authority, CA),通常直接由叢集管理,藉此確保資訊的真實性。雖然 X.509 標準有定義憑證的撤銷機制,但目前 Kubernetes 並沒有相關實做,導致只能利用簽發短效期的憑證,達到金鑰洩漏場景的減災。缺乏撤銷機制也使得這個驗證方法不易修改身份資訊。由於客戶端憑證較不方便使用,尤其需要透過頻繁換發短效期憑證達到安全性,相關自動化成本較高,除了叢集內部元件的身份驗證外,較不常用於使用者端,多見於非正式環境,或是另外嚴謹保存作為管理者的備用手段。
Kubernetes 中的 X.509 憑證即為 TLS 客戶端憑證 (client certificate),在 TLS 的驗證模型下,除了伺服器端需要提供憑證證明本身的真實性以外,客戶端也可以提供憑證,使伺服器端得以驗證客戶端的身份 (client certificate authentication, CCA)。此機制近期又以 mutual TLS (mTLS) 的名稱流行,強調雙方都可以驗證彼此的身份。Kubernetes 將身份名稱與所在群組存放在客戶端憑證的 Subject 屬性內,並且要求憑證來自特定的簽發單位 (certificate authority, CA),通常直接由叢集管理,藉此確保資訊的真實性。雖然 X.509 標準有定義憑證的撤銷機制\cite{cooper2008internet}\cite{santesson2013x},但目前 Kubernetes 並沒有相關實做,導致只能利用簽發短效期的憑證,達到金鑰洩漏場景的減災。缺乏撤銷機制也使得這個驗證方法不易修改身份資訊。由於客戶端憑證較不方便使用,尤其需要透過頻繁換發短效期憑證達到安全性,相關自動化成本較高,除了叢集內部元件的身份驗證外,較不常用於使用者端,多見於非正式環境,或是另外嚴謹保存作為管理者的備用手段。

Authenticating proxy 透過在 Kubernetes API 之前加一層代理伺服器進行驗證邏輯。在將請求轉發給 Kubernetes API 時,代理伺服器會加上約定的 HTTP headers 以表達使用者身份。對於代理伺服器驗證使用者的方式並沒有限制。這個方法等同於將驗證邏輯直接抽出另外自行實做,相當富有彈性,但開發成本較高,並且需自行注意撤銷等機制是否完善。

其餘的手段皆是透過 token 進行身份驗證,但 token 本身有所不同。Static tokens 是在 Kubernetes 本身預先設定好一個 token 對應身份資訊的清單,token 本身通常採用一個隨機的字串。由於目前更動這份清單需要重新啟動 kube-apiserver,這個方法較少叢集採用。Webhook tokens 則是透過管理者提供的 HTTP API 將 token 對應到身份資訊,比 static tokens 來的有彈性,可以動態更動而不須重啟 kube-apiserver。

ServiceAccount tokens 與 OpenID Connect tokens 則都是採用 JSON Web Token (JWT),一個有經過簽章的 token 格式,裡面包含身份資訊與效期等,Kubernetes 透過驗證簽章來確保真實性。ServiceAccount 是一個 resource 種類,代表一個身份,但不支援群組,由其可以簽發 ServiceAccount tokens。刪除 ServiceAccount resources 可以撤銷全部由其發出的 tokens,簽發 token 時也可以進一步綁定 Secrets\footnote{Secret: Resource 種類,提供資料儲存,本身並不帶有任何功能。} 或 Pods,使用當下會額外檢查這些 resources 是否存在,達成以 token 為單位的撤銷。由於可以透過 Kubernetes API 管理,ServiceAccount tokens 常用於自動化場景,也常用於 controllers 元件。因為 ServiceAccount tokens 採用標準的 JWT 格式,透過簽章即可確認真偽,也有自 Kubernetes 存取外部系統時,供外部系統驗證來源的應用。
ServiceAccount tokens 與 OpenID Connect tokens 則皆採用 JSON Web Token (JWT)\cite{jones2015json},一個有經過簽章的 token 格式,裡面包含身份資訊與效期等,Kubernetes 透過驗證簽章來確保真實性。ServiceAccount 是一個 resource 種類,代表一個身份,但不支援群組,由其可以簽發 ServiceAccount tokens。刪除 ServiceAccount resources 可以撤銷全部由其發出的 tokens,簽發 token 時也可以進一步綁定 Secrets\footnote{Secret: Resource 種類,提供資料儲存,本身並不帶有任何功能。} 或 Pods,使用當下會額外檢查這些 resources 是否存在,達成以 token 為單位的撤銷。由於可以透過 Kubernetes API 管理,ServiceAccount tokens 常用於自動化場景,也常用於 controllers 元件。因為 ServiceAccount tokens 採用標準的 JWT 格式,透過簽章即可確認真偽,也有自 Kubernetes 存取外部系統時,供外部系統驗證來源的應用。

OpenID Connect (OIDC) 則是一個基於 OAuth 框架衍生的身份驗證協定,廣泛運用於網頁應用場景,由中心化的伺服器進行驗證,將身份資訊授予其他應用使用,並且可以讓使用者管理對應用存取身份資料的授權。Kubernetes 本身並不實做 OIDC 協定,而是利用此機制下,OIDC 伺服器所簽發的 JWT(在協定上以 \verb|id_token| 稱呼)進行驗證,並預期由客戶端處理 OIDC 協定流程。由於可以跟現有 OIDC 伺服器整合,實做容易且不須額外的使用者註冊流程,常用於使用者的互動存取。
OpenID Connect (OIDC)\cite{sakimura2014openid} 則是一個基於 OAuth 框架衍生的身份驗證協定,廣泛運用於網頁應用場景,由中心化的伺服器進行驗證,將身份資訊授予其他應用使用,並且可以讓使用者管理對應用存取身份資料的授權。Kubernetes 本身並不實做 OIDC 協定,而是利用此機制下,OIDC 伺服器所簽發的 JWT(在協定上以 \verb|id_token| 稱呼)進行驗證,並預期由客戶端處理 OIDC 協定流程。由於可以跟現有 OIDC 伺服器整合,實做容易且不須額外的使用者註冊流程,常用於使用者的互動存取。

\begin{table}[htb]
    \centering


@@ 94,4 94,4 @@ WebAssembly\cite{haas2017bringing} (簡稱 WASM)是在瀏覽器上的一個

% 以下講了沒啥用 不講又覺得缺了點什麼

WebAssembly 近期也發展出網頁生態以外的應用。透過 WebAssembly 與外界溝通的能力,只須定義 Application Binary Interface (ABI),即可建構一個擴充元件的機制,使得軟體產品能夠利用多種不同的程式語言擴充邏輯,並且不受運行環境的硬體架構限制,比起傳統使用 C 語言動態載入函式庫作為中介的方式來的更有彈性。
WebAssembly 近期也發展出網頁生態以外的應用\cite{spies2021evaluation}。透過 WebAssembly 與外界溝通的能力,只須定義 Application Binary Interface (ABI),即可建構一個擴充元件的機制,使得軟體產品能夠利用多種不同的程式語言擴充邏輯,並且不受運行環境的硬體架構限制,比起傳統使用 C 語言動態載入函式庫作為中介的方式來的更有彈性。

M Sections/4.Architecture.tex => Sections/4.Architecture.tex +7 -7
@@ 147,7 147,7 @@ ResourceQuota 所能管控的資源大致有兩種:運算資源以及 resource

\section{網頁界面實做}

進一步對網頁界面的需求分析,可以將網頁界面切分為兩大塊:支援 Helm 的通用 Kubernetes 網頁型客戶端,以及 CSKloud 特定的部份,包含權限開通元件的整合以及 CSKloud 平台面向使用者的文件等。其中 Kubernetes 客戶端很容易的就可以利用於其他場景,我們採取 open-core 的策略,將其開放原始碼,以 MIT 授權條款\footnote{MIT license: 一個在網頁技術場域廣受歡迎的寬鬆型開放原始碼條款。}釋出,命名為 Sparkles\footnote{Sparkles 釋出於 \url{https://github.com/xdavidwu/sparkles} 。},回饋於社會,使得非平台使用者也能受益,同時也利用開放原始碼社群的力量來茁壯平台的發展。
進一步對網頁界面的需求分析,可以將網頁界面切分為兩大塊:支援 Helm 的通用 Kubernetes 網頁型客戶端,以及 CSKloud 特定的部份,包含權限開通元件的整合以及 CSKloud 平台面向使用者的文件等。其中 Kubernetes 客戶端很容易的就可以利用於其他場景,我們採取 open-core\cite{hall2016open} 的策略,將其開放原始碼,以 MIT 授權條款\footnote{MIT license: 一個在網頁技術場域廣受歡迎的寬鬆型開放原始碼條款。}釋出,命名為 Sparkles\footnote{Sparkles 釋出於 \url{https://github.com/xdavidwu/sparkles} 。},回饋於社會,使得非平台使用者也能受益,同時也利用開放原始碼社群的力量來茁壯平台的發展。

\begin{figure}[thb]
    \centering


@@ 157,7 157,7 @@ ResourceQuota 所能管控的資源大致有兩種:運算資源以及 resource

網頁界面採用 Vue.js\cite{vue} 框架,以 TypeScript 語言\footnote{TypeScript: 一個轉譯為 JavaScript 的語言,比起 JavaScript 多了型別標示以利開發。}撰寫,使用 Vuetify 做為元件庫。Vuetify 的元件設計採用基於 Google 提出的 Material Design 設計語彙,同時 Kubernetes 也是由 Google 發跡的,在些許程度上,選擇 Vuetify 帶給了 Kubernetes 使用者更契合的親切感。由於界面希望帶有在容器內執行指令的功能,所需的終端機傳統上採用黑底白字配色,為避免其元件顯得突兀,整體界面亦採暗色系設計。在流行上,近期資訊界生產力相關的網頁服務也有逐漸導入暗色系設計的趨勢。

對於 Kubernetes API 的操作,我們採用由 Kubernetes 提供的 OpenAPI\footnote{OpenAPI: 一種用於描述 RESTful API 行為的框架。} 定義檔,透過 openapi-generator\footnote{openapi-generator: 由 OpenAPI 定義檔產生適用於各種不同語言的函式庫的工具。} 自動產生的函式庫輔助開發。在網頁的各個功能,我們皆有採用 Kubernetes 的 watch 機制達到資料的即時更新。Kubernetes 的 watch 是一個特有的 HTTP API 呼叫方法,透過維持長期不中斷的 HTTP response,藉由 HTTP 的 chunked transfer\footnote{Chunked transfer: 一個將內容分塊傳送的 HTTP 編碼機制,常用於串流等持續產生資料的場合。} 編碼,即時的將一個 resources 列表的內容更新以新增、移除、修改 resource 等事件傳出,並且每個事件帶有一個版本號,如果 HTTP 通訊意外中止,客戶端可以透過這個版本號代表目前對 resources 列表的認知,向 Kubernetes 重發一個 watch 呼叫獲取由該版本後的變更。這個機制也廣泛運用在 Kubernetes 元件內部的溝通。
對於 Kubernetes API 的操作,我們採用由 Kubernetes 提供的 OpenAPI\footnote{OpenAPI: 一種用於描述 RESTful API 行為的框架。}\cite{openapi} 定義檔,透過 openapi-generator\footnote{openapi-generator: 由 OpenAPI 定義檔產生適用於各種不同語言的函式庫的工具。} 自動產生的函式庫輔助開發。在網頁的各個功能,我們皆有採用 Kubernetes 的 watch 機制達到資料的即時更新。Kubernetes 的 watch 是一個特有的 HTTP API 呼叫方法,透過維持長期不中斷的 HTTP response,藉由 HTTP 的 chunked encoding\footnote{Chunked encoding: 一個將內容分塊傳送的 HTTP 編碼機制,常用於串流等持續產生資料的場合\cite{swaminathan2011low}。} 編碼,即時的將一個 resources 列表的內容更新以新增、移除、修改 resource 等事件傳出,並且每個事件帶有一個版本號,如果 HTTP 通訊意外中止,客戶端可以透過這個版本號代表目前對 resources 列表的認知,向 Kubernetes 重發一個 watch 呼叫獲取由該版本後的變更。這個機制也廣泛運用在 Kubernetes 元件內部的溝通。

\begin{figure}[htb]
    \centering


@@ 238,9 238,9 @@ Helm Release 預設的 Secret 儲存方式需要經過 Base64\footnote{Base64: 

界面上以 Helm Releases 表格呈現為主,可以展開觀看版本歷史,在每個項目旁有解除安裝、回滾、更新等按鈕,解除安裝會保留歷史,仍然可以將其裝回,也可以事後刪除歷史。畫面的右下角則是有浮動式的按鈕,可以觸發安裝新 Helm Release 的流程。

對於安裝的流程,按下浮動式按鈕後,會跳出對話框進行三個步驟:第一步是選擇要安裝的 Helm Chart,由一個可搜尋的列表選取。第二步是填入 Helm Values,如同 Helm CLI 以 YAML\footnote{YAML: 一個資料標記語言,在 Kubernetes 生態系頗為常用,尤其是用來表達 Kubernetes resources。} 的形式填入,界面上提供三個頁簽,一是填寫 Helm Values 的編輯器,二是來自 Helm Chart 的 README 檔案,通常會在此提供常用選項的說明,三是 Helm Chart 內的 Helm Values 預設值檔案,慣例在此以註解的形式完整地對選項欄位說明。以 CSKloud 的場景,編輯器會自動帶入針對 CSKloud 的第二層預設值,這個預設值來自測試安裝時所帶入的參數,調整如資源配額用量等選項,以確保在平台的限制下得以安裝,使用者也可以進一步進行修改。最後的步驟則是為此 Helm Release 命名,界面會自動預設一個隨機的名字。
對於安裝的流程,按下浮動式按鈕後,會跳出對話框進行三個步驟:第一步是選擇要安裝的 Helm Chart,由一個可搜尋的列表選取。第二步是填入 Helm Values,如同 Helm CLI 以 YAML\cite{yaml}\footnote{YAML: 資料序列化語言,語法為 JSON 的超集,於 Kubernetes 常用來表達 resources。} 的形式填入,界面上提供三個頁簽,一是填寫 Helm Values 的編輯器,二是來自 Helm Chart 的 README 檔案,通常會在此提供常用選項的說明,三是 Helm Chart 內的 Helm Values 預設值檔案,慣例在此以註解的形式完整地對選項欄位說明。以 CSKloud 的場景,編輯器會自動帶入針對 CSKloud 的第二層預設值,這個預設值來自測試安裝時所帶入的參數,調整如資源配額用量等選項,以確保在平台的限制下得以安裝,使用者也可以進一步進行修改。最後的步驟則是為此 Helm Release 命名,界面會自動預設一個隨機的名字。

調整 Helm Values 的編輯器採用 CodeMirror 開源編輯器專案,除了 YAML 的基本語法高亮以外,我們採用 codemirror-json-schema 擴充元件提供進一步的資料驗證、欄位說明與名稱自動補全。codemirror-json-schema 收取 JSON Schema 作為欄位定義,JSON Schema 是一個描述 JSON\footnote{JSON: 資料標記語言,與 YAML 的模型類似,語法較為容易以程式解析。} 資料的定義框架,但由於大多數 YAML 應用場景描述的資料也都可以由 JSON 的形式表達,也常用來描述 YAML 資料。部份 Helm Chart 會提供對其 Helm Values 的 JSON Schema 定義檔。
調整 Helm Values 的編輯器採用 CodeMirror 開源編輯器專案,除了 YAML 的基本語法高亮以外,我們採用 codemirror-json-schema 擴充元件提供進一步的資料驗證、欄位說明與名稱自動補全。codemirror-json-schema 收取 JSON Schema 作為欄位定義,JSON Schema\cite{pezoa2016foundations} 是一個描述 JSON\cite{bray2017rfc}\footnote{JSON: 資料序列化語言,語法比 YAML 容易以程式解析。} 資料的定義框架,但由於大多數 YAML 應用場景描述的資料也都可以由 JSON 的形式表達,也常用來描述 YAML 資料。部份 Helm Chart 會提供對其 Helm Values 的 JSON Schema 定義檔。

\subsection{Quotas 視覺化}



@@ 290,11 290,11 @@ CSKloud 開放使用者直接存取 Kubernetes API,並且提供 kubectl 連線
    \caption{CSKloud v3 網頁界面 Resource Explorer 頁面}
\end{figure}

Resource Explorer 提供對 Kubernetes resource 列舉、創立、編輯、刪除的操作界面,作為使用者與 Kubernetes 互動中最基礎的功能。這個頁面採用 Kubernetes API discovery 機制,在運行時會查詢叢集所有支援的 resource 種類,未來如果隨著 Kubernetes 演進而對種類有所增減,這個頁面不須經過修改可以直接沿用。
Resource Explorer 提供對 Kubernetes resource 列舉、創立、編輯、刪除的操作界面,作為使用者與 Kubernetes 互動中最基礎的功能。這個頁面透過 Kubernetes discovery API 在運行時查詢叢集所有支援的 resource 種類,未來如果隨著 Kubernetes 演進而對種類有所增減,這個頁面不須經過修改可以直接沿用。

這個頁面採用 Kubernetes API 本身的製表功能,可以對 resources 產出基本的概要,kubectl 的表格呈現亦是基於此機制。使用者可以將游標移動到表格欄位名稱上方,透過提示框了解欄位意義,也可以點擊針對欄位排序。右下方的浮動按鈕可以撰寫新的 resource,點擊表格列則是查看對應的 resource 的完整內容,以 YAML 的形式呈現。在單一 resource 完整呈現中右下方的浮動按鈕功能列表,也有編輯與刪除等功能。
Resources 的列舉採用 Kubernetes 內建的製表功能,可以產出基本的概要,kubectl 的表格呈現亦是基於此機制。使用者可以將游標移動到表格欄位名稱上方,透過提示框了解欄位意義,也可以點擊針對欄位排序。右下方的浮動按鈕可以撰寫新的 resource,點擊表格列則是查看對應的 resource 的完整內容,以 YAML 的形式呈現。在單一 resource 完整呈現中右下方的浮動按鈕功能列表,也有編輯與刪除等功能。

在 YAML 檢視與編輯的方面,則與 \ref{sec:helm} Helm 頁面功能相似,採用 CodeMirror,並且一樣有基於 JSON Schema 的資料驗證、欄位提示與名稱補全。這裡的欄位定義則是來自於 Kubernetes API 的 OpenAPI 定義檔,由相關 API endpoint 的定義萃取轉換而成。Kubernetes 具有 OpenAPI Discovery 功能,可以從 API 獲取定義檔,如同 API Discovery,這樣的機制使得網頁不須修改,即可適用於未來的 Kubernetes 版本。
在 YAML 檢視與編輯的方面,則與 \ref{sec:helm} Helm 頁面功能相似,採用 CodeMirror,並且一樣有基於 JSON Schema 的資料驗證、欄位提示與名稱補全。這裡的欄位定義則是來自於 Kubernetes API 的 OpenAPI 定義檔,由描述相關 API endpoint 的定義萃取轉換而成。Kubernetes 對於 OpenAPI 定義亦具有 discovery 機制,可以從 API 獲取定義檔,這樣的形式同樣使得網頁不須修改,即可適用於未來的 Kubernetes 版本。

創立新 resource 時,網頁會針對目前選擇的 resource 種類,預先填好必填或者常用的欄位,也會取好一個暫定名稱。預填欄位較為難以進行動態判斷,因此採用人工逐一事先設計的方式。如果選定的種類未先設計模板,仍會自動預填 Kubernetes resource 共通的 \verb|apiVersion|、\verb|kind|、\verb|metadata| 欄位。


M main.tex => main.tex +2 -0
@@ 156,7 156,9 @@


\bibliographystyle{IEEEtran}
\begin{onehalfspacing}
\bibliography{ref}
\end{onehalfspacing}

% APA
%\printbibliography

M ref.bib => ref.bib +89 -2
@@ 17,7 17,7 @@
}

% XXX quotes missing on online/electornic in IEEEtran
% TODO dot position, urldate
% TODO dot position, urldate, unify rfcs

@online{kubernetes,
    title 	= {``{Kubernetes}''},


@@ 68,7 68,94 @@
}

@techreport{fette2011websocket,
    title	= {The websocket protocol},
    title	= {The {WebSocket} protocol},
    author	= {Fette, Ian and Melnikov, Alexey},
    year	= {2011},
}

@article{jones2015json,
    title	= {{JSON Web Token (JWT)}},
    author	= {Jones, Michael},
    journal	= {Internet Engineering Task Force (IETF) RFC},
    volume	= {7519},
    year	= {2015},
}

@article{sakimura2014openid,
    title	= {{OpenID Connect Core} 1.0 incorporating errata set 1},
    author	= {Sakimura, Nat and Bradley, John and Jones, Mike and De Medeiros, Breno and Mortimore, Chuck},
    journal	= {The OpenID Foundation, specification},
    volume	= {335},
    year	= {2014},
}

@inproceedings{swaminathan2011low,
    title	= {Low latency live video streaming using {HTTP} chunked encoding},
    author	= {Swaminathan, Viswanathan and Wei, Sheng},
    booktitle	= {2011 IEEE 13th International Workshop on Multimedia Signal Processing},
    pages	= {1--6},
    year	= {2011},
    organization	= {IEEE},
}

@techreport{cooper2008internet,
    title	= {Internet {X.509} public key infrastructure certificate and certificate revocation list {(CRL)} profile},
    author	= {Cooper, David and Santesson, Stefan and Farrell, Stephen and Boeyen, Sharon and Housley, Russell and Polk, William},
    year	= {2008},
}

@techreport{santesson2013x,
    title	= {{X.509} {Internet} public key infrastructure online certificate status protocol - {OCSP}},
    author	= {Santesson, Stefan and Myers, Michael and Ankney, Rich and Malpani, Ambarish and Galperin, Slava and Adams, Carlisle},
    year	= {2013},
}

@inproceedings{spies2021evaluation,
    title	= {An evaluation of {WebAssembly} in non-web environments},
    author	= {Spies, Benedikt and Mock, Markus},
    booktitle	= {2021 XLVII Latin American Computing Conference (CLEI)},
    pages	= {1--10},
    year	= {2021},
    organization	= {IEEE},
}

@article{hall2016open,
    title	= {Open-source licensing and business models: Making money by giving it away},
    author	= {Hall, Andrew J},
    journal	= {Santa Clara Computer \& High Tech. LJ},
    volume	= {33},
    pages	= {427},
    year	= {2016},
    publisher	= {HeinOnline},
}

@online{openapi,
    title	= {{OpenAPI} Specification v3.0.0},
    author	= {Whitlock, Jeremy and Gardiner, Marsh and Ratovsky, Ron and Tam, Tony and Harmon, Jason},
    year	= {2017},
    url		= {https://spec.openapis.org/oas/v3.0.0.html},
    urldate	= {2024-09-25},
}

@inproceedings{pezoa2016foundations,
    title	= {Foundations of {JSON Schema}},
    author	= {Pezoa, Felipe and Reutter, Juan L and Suarez, Fernando and Ugarte, Mart{\'\i}n and Vrgo{\v{c}}, Domagoj},
    booktitle	= {Proceedings of the 25th international conference on World Wide Web},
    pages	= {263--273},
    year	= {2016},
}

@online{yaml,
    title	= {{YAML} ain't markup language {(YAML™)} version 1.2, revision 1.2.2},
    author	= {Ben-Kiki, Oren and Evans, Clark and döt Net, Ingy},
    year	= {2021},
    url		= {https://yaml.org/spec/1.2.2/},
    urldate	= {2024-09-25},
}

@misc{bray2017rfc,
    title	= {{RFC} 8259: The {JavaScript} object notation {(JSON)} data interchange format},
    author	= {Bray, Tim},
    year	= {2017},
    publisher	= {RFC Editor},
}