콘텐츠로 이동

경로 규칙 코덱

경로 규칙 코덱은 Dojang이 파일을 중간 스냅샷과 대상 경로에 쓰기 전에 저장소의 원본 파일을 변환합니다. 저장소에는 원본 바이트를 유지하고, 중간 스냅샷에는 렌더링된 바이트를 저장합니다.

Dojang에는 다섯 가지 내장 코덱이 있습니다. identity는 바이트를 바꾸지 않으며 기존의 모든 선언 파일에서 기본값입니다. template은 결정적인 텍스트 템플릿을 렌더링합니다. 비밀 값 코덱인 encrypted, encrypted-re-add, secret-template은 선언 파일에 등록한 백엔드를 좁은 바이너리 프로토콜로 호출합니다. 다른 코덱 이름은 Dojang 라이브러리를 사용하는 애플리케이션이 그 구현을 등록한 경우에만 쓸 수 있습니다.

선언 파일 문법

상세 경로 규칙 분기에만 codec을 선언할 수 있습니다. 문자열 형식은 설정이 없는 코덱을 선택합니다:

[[files."git/config"]]
moniker = "posix"
path = "~/.gitconfig"
codec = "identity"

객체 형식은 정규화된 설정을 전달합니다. 파서는 인라인 객체를 받아들이고, 선언 파일 작성기는 같은 뜻의 중첩 테이블 형식으로 기록합니다:

[[files."app/config"]]
when = "os = linux"
path = "~/.config/app/config"

[files."app/config".codec]
name = "example"

[files."app/config".codec.config]
format = "toml"
strict = true

설정 값에는 문자열, 정수, 불리언, 배열, 중첩 테이블을 쓸 수 있습니다. 같은 설정이 언제나 하나의 결정적 식별값을 갖도록 부동 소수점 수와 날짜·시각 값은 거부합니다.

코덱을 선언한 분기에는 path가 필요하며 공 경로 규칙에는 코덱을 선언할 수 없습니다. kind = "symlink" 분기에는 기본 identity 코덱만 쓸 수 있습니다. 배포 링크는 계속해서 저장소 원본을 직접 가리키는 단방향 투영입니다.

템플릿 코덱

문자열 형식으로 내장 템플릿 코덱을 선택합니다. 별도 설정은 없습니다:

[vars]
git_name = "Ada"

[[files."git/config"]]
when = "always"
path = "~/.gitconfig"
codec = "template"

원본 파일은 UTF-8 텍스트입니다. 대소문자를 구분하는 정확한 선언 변수 이름은 vars로, 대소문자를 구분하지 않는 머신 정보 이름은 facts로 읽습니다:

[user]
    name = {{ vars.git_name }}
{% if facts.os == "linux" %}
[credential]
    helper = libsecret
{% endif %}

vars에는 선언 파일에서 현재 활성화된 선언만 들어갑니다. 프로세스 환경으로 대체하지 않습니다. 실행이 값을 읽는 표현식에 도달한 경우에만 없는 값이 오류가 되므로, 선택되지 않은 조건 분기에서는 사용할 수 없는 값을 참조해도 됩니다. 오류에는 원본이나 렌더링 결과를 표시하지 않고 1부터 시작하는 원본 줄과 열을 표시합니다. 없는 값 오류에는 vars.git_name이나 facts.os처럼 네임스페이스를 유지합니다. 없는 멤버 이름을 동적으로 계산한 경우에는 실행 중에 얻은 키를 노출하는 대신 오류에 <dynamic>을 사용합니다. 선택한 선언 변수가 올바른 플랫폼 텍스트가 아니면 값이나 관련 없는 템플릿 위치를 출력하지 않고 vars 이름을 오류에 표시합니다.

지원하는 언어는 Ginger/Jinja 문법의 순수한 부분집합입니다:

  • 리터럴, 주석, 보간, 목록, 사전;
  • if/elif/else, switch, 유한 for 반복, 로컬 set, scope, indent 문;
  • 인덱싱, 삼항식, 산술·비교·불리언 연산, 결정적인 텍스트·수치·컬렉션·서술어 내장 함수.

로컬 이름은 해당 set 문 뒤에서만 사용할 수 있습니다. 조건문 뒤에서는 가능한 모든 분기에서 새 이름에 값을 할당한 경우에만 그 이름을 사용할 수 있습니다.

include, import, 상속, block, macro, 동적 호출 대상, Ginger의 모든 공백 제어 및 주석 선행 형식으로 작성한 script, do, lambda, 예외 처리와 eval, throw, 날짜, 정규식, 이스케이프, JSON, 고차 함수처럼 효과가 있거나 표현 방식에 따라 달라지는 내장 함수는 거부합니다. 혼합된 줄바꿈과 마지막 줄바꿈을 보존합니다. 검사되지 않은 Ginger 실행 오류를 일으킬 수 있는 center, divisibleby, 정수 나눗셈(// 또는 int_ratio), 나머지 연산(% 또는 modulo), printf 방식 서식 지정(format 또는 printf), 텍스트 치환 (replace)도 지원하지 않습니다. --force를 써도 템플릿 반영은 거부되므로 저장소의 템플릿을 직접 편집해야 합니다.

코덱 백엔드

선언 파일에 재사용할 백엔드를 선언한 뒤 비밀 값 코덱에서 그 키를 사용합니다.

[codec-backends.age]
command = "$HOME/.local/bin/dojang-age-backend"
version = "age-1.2-profile-3"
timeout-seconds = 30
options = { identity = "work" }

명령 경로는 절대 경로로 확장되어야 합니다. Dojang은 셸이나 인자 없이 명령을 직접 시작하고, 빈 환경과 저장소 루트 작업 디렉터리를 사용합니다. Windows의 프로세스 동작과 소유자 전용 저장을 검증하기 전까지는 Windows에서 백엔드 선언을 쓸 수 없습니다.

표준 입력은 UTF-8 JSON 한 줄로 시작합니다. 이 줄에는 dojang-codec-backend-v1 프로토콜, 선언 파일 안의 백엔드 이름, 선언한 버전, 연산, 비밀 값이 아닌 옵션이 들어갑니다. 조회에는 item 필드도 들어갑니다. 줄바꿈 뒤에는 정확한 바이너리 페이로드가 이어집니다. decrypt에는 암호화된 원본 바이트, encrypt에는 배치된 바이트, lookup에는 빈 페이로드를 전달합니다. 백엔드는 요청받은 바이트만 표준 출력에 쓰고 종료 코드 0으로 끝내야 합니다. 실패하면 0이 아닌 종료 코드와 함께 표준 오류에 JSON 객체 하나를 쓸 수 있으며, code에는 missing-item, invalid-input, permission-denied, unavailable 중 하나를 사용합니다. Dojang은 그 밖의 백엔드 출력을 버리고 허용 목록에 있는 메시지만 보고합니다.

기본 제한 시간은 30초이며 선언 파일에서 1초부터 300초까지 지정할 수 있습니다. 시간 초과, 중단, 시작 실패, 잘못된 진단, 0이 아닌 종료 코드는 동기화가 시작되기 전에 코덱 평가를 실패시킵니다. 백엔드는 표준 입력으로만 비밀 값을 받고 표준 출력으로만 돌려줍니다. 백엔드 자체의 로그, 크래시 보고서, 임시 파일에 비밀 값을 남기지 않는 일은 백엔드 구현의 책임입니다.

암호화 코덱

encrypted는 저장소에 암호문을 보관하고 백엔드가 복호화한 바이트를 배치합니다. 반영 정책은 reject입니다. 비밀 값 경로 규칙은 소유자 전용 모드를 명시적으로 선택해야 합니다.

[[files."ssh/id_ed25519"]]
when = "always"
path = "~/.ssh/id_ed25519"
mode = "private"

[files."ssh/id_ed25519".codec]
name = "encrypted"

[files."ssh/id_ed25519".codec.config]
backend = "age"

encrypted-re-add는 같은 설정을 사용하지만 반영을 허용합니다. 변경된 대상 바이트를 백엔드의 encrypt 연산에 전달한 뒤, 만들어진 암호문을 다시 복호화해 대상과 정확히 같을 때만 받아들입니다. 무작위 암호화도 지원합니다. 원본 변경은 암호문이 같은지가 아니라 복호화한 의미상 바이트를 기준으로 판단합니다. 대상에서 저장소로 반영하는 작업이 의도된 절차가 아니라면 기본 encrypted를 사용하세요.

비밀 값 템플릿 코덱

secret-template은 결정적 템플릿 언어에 정적인 인자 두 개를 받는 함수를 추가합니다.

token = {{ secret("vault", "services/example/token") }}

두 인자는 모두 문자열 리터럴이어야 합니다. 첫 번째 인자는 codec-backends 항목을, 두 번째 인자는 백엔드의 항목 키를 지정합니다. 템플릿은 조회한 값이 아니라 참조를 저장소에 보관합니다. 조회는 필요한 분기에서만 실행되므로 선택되지 않은 조건 분기는 백엔드에 접근하지 않습니다. 한 평가에서 같은 참조를 반복해서 사용하면 메모리 안의 결과를 재사용합니다. 반영은 항상 거부합니다.

경로 규칙에는 mode = "private" 또는 mode = "private-executable"을 선언해야 합니다. 뒤의 모드는 배치 파일에 실행 권한이 꼭 필요한 경우에만 사용하세요.

비밀 값 데이터 경계

암호화된 저장소 바이트와 비밀 값 참조는 공유할 수 있습니다. 복호화한 바이트와 조회한 값은 프로세스 메모리에 존재하며 중간 스냅샷과 대상 경로에 기록됩니다. Dojang은 소유자 전용 경로 규칙 모드를 명시적으로 요구하고, 그 모드를 중간 파일에 적용합니다. 새 중간·대상 내용은 소유자 전용 임시 파일에 쓴 뒤 게시하며, 소유자 전용 트랜잭션 디렉터리 아래에 관리 대상 기준 사본을 저장합니다. 비밀 값 코덱 결과와 의존성 메타데이터는 일반 코덱 캐시에 기록하지 않고 해당 명령 안에서만 유지합니다. 내장 diff는 비밀 값 본문을 숨기며 외부 diff 프로그램은 이를 읽을 수 없습니다.

이 파일 권한은 저장 데이터 암호화가 아닙니다. 권한 있는 계정, 잘못 설정한 백업, 스왑, 파일 시스템 스냅샷, 임시 파일을 쓰는 백엔드는 여전히 평문을 남길 수 있습니다. 대상 경로와 Dojang 상태 위치를 이에 맞게 선택하고, 백엔드 실행 파일과 키 자료도 따로 보호하세요.

--dry-run은 예전에 만든 영구 코덱 항목이 있어도 비밀 값 백엔드에 접근하지 않습니다. 계획한 바이트를 아는 것처럼 가장하지 않고 코덱에 유효한 결과가 필요하다고 보고합니다. 온라인 평가를 의도할 때만 명령을 일반 모드로 실행하세요.

백엔드 실패는 원본, 중간 스냅샷, 대상 경로, 머신 상태를 바꾸기 전에 발생합니다. 시간 초과나 실패 뒤에는 백엔드 접근을 고치고 같은 명령을 다시 실행하세요. 대상의 평문을 암호화된 원본 위에 복사하면 안 됩니다. 백엔드, 백업, 크래시 보고서로 평문이 유출되었을 가능성이 있다면 해당 비밀 값을 교체하고 다음 적용 전에 저장소 원본을 다시 암호화하세요.

평가와 캐시

Dojang은 명령이 선택한 경로 규칙만 평가합니다. 코덱은 원본 바이트, 정규화된 설정, 그리고 자신이 선언한 머신 정보·선언 변수·통제된 외부 입력만 받습니다. 선택된 비-identity 경로 규칙은 원본이 없어도 코덱 등록과 설정을 검증합니다. 원본 내용에 의존하는 분석은 원본이 일반 파일일 때만 실행합니다. 사용한 각 입력의 지문은 결정적 캐시 키에 포함됩니다. 사용하지 않은 머신 정보나 선언 변수가 바뀌어도 결과를 무효화하지 않습니다. 사용자 정의 코덱에 전달하는 선언 변수 값은 운영체제 문자열의 원문 바이트를 유지하며, POSIX에서 UTF-8이 아닌 값도 그대로 전달합니다. 템플릿 코덱은 선택한 값을 엄격한 플랫폼 텍스트로 디코딩합니다. 머신 정보에는 환경 서술어와 같은 os, arch, kernel, kernel-release, hostname 키를 씁니다. 사용자 정의 머신 정보의 키는 환경 서술어 이름에서 fact. 뒤에 오는 부분입니다.

순수 코덱 변환은 코덱 검증을 통과한 경로 규칙 설정을 받습니다. 변환 함수는 애플리케이션 모나드, 파일 시스템, 터미널, 프로세스 API를 사용할 수 없습니다. 효과가 있는 코덱 프로그램은 실행 환경이 해석하는 외부 입력만 요청할 수 있으며, 내장 비밀 값 코덱은 이 경계를 통해 백엔드 연산을 실행합니다.

dojang apply는 렌더링된 바이트를 중간 스냅샷 및 실제 대상과 비교합니다. 계획을 세우는 동안 한 번 평가하고, 그 결과를 그대로 중간 스냅샷에 쓴 다음 스냅샷을 대상에 배치합니다. dojang status, dojang diff, dojang reflect, dojang edit도 같은 렌더링된 원본 보기를 사용합니다. 한 명령 안에서는 원본이 그대로인 동안 평가 결과를 재사용합니다. 평가할 때 선택한 머신 정보, 선언 변수, 외부 입력은 해당 명령이 끝날 때까지 고정합니다. 반영의 역변환과 왕복 검증도 같은 입력을 사용합니다. 계획 실행 전과 버퍼에 보관한 코덱 결과를 쓰기 직전에, Dojang은 기준이 된 파일이 평가 당시의 바이트와 같은지 확인합니다. 바이트가 다르면 쓰기를 중단합니다. 내장 diff는 원본 바이트를 노출하지 않으며 UTF-8이 아닌 출력은 바이너리 차이로 보고합니다. 비밀 값 코덱의 본문은 항상 숨깁니다. 렌더링된 내용이나 비밀 값 내용을 외부 diff 프로그램에 노출해야 하는 경우에는 외부 diff 프로그램 실행을 거부합니다.

머신 상태 스키마 버전 6은 코덱 이름과 버전, 설정 다이제스트, 캐시 키, 의존성 지문을 저장할 수 있습니다. 원본 바이트나 렌더링된 바이트의 두 번째 사본은 저장하지 않습니다. 캐시 키가 일치하고 중간 스냅샷이 기록된 파일 지문과 여전히 일치할 때만 캐시를 재사용합니다. 수렴 상태를 기록하기 전에는 원본이 평가에 사용한 바이트와 여전히 같은지도 확인합니다.

코덱은 캐시가 없는 상태에서 --dry-run 평가를 허용할지 선언합니다. 순수 코덱은 평가할 수 있습니다. 캐시 전용 코덱은 유효한 캐시가 없으면 모의 실행을 실패시키므로, 계산하지 않은 결과를 계산한 것처럼 보고하지 않습니다. 코덱이 외부 입력을 선언했다면 Dojang은 이를 해석하기 전에 모의 실행을 실패시킵니다. 외부 입력 해석기를 호출하지 않고는 그 입력의 지문을 검증할 수 없기 때문입니다. 캐시 전용 re-add 코덱은 새로 배치된 바이트의 역변환과 검증을 정방향 캐시로 대신할 수 없으므로 모의 실행 중 반영도 거부합니다.

반영

반영 정책은 선언 파일이 아니라 등록된 코덱 구현이 정합니다:

  • identity는 배치된 바이트를 저장소로 그대로 반영합니다.
  • reject는 선택한 파일을 하나도 바꾸기 전에 반영을 중단합니다. --force도 이 정책을 우회하지 않습니다.
  • re-add는 배치된 바이트를 저장소 표현으로 역변환합니다. Dojang은 그 결과를 곧바로 정방향 변환하고, 배치된 바이트를 정확히 재현할 때만 받아들입니다. 대상이 없으면 저장소 파일을 삭제합니다.

기본 제공 identity가 아닌 코덱을 쓰는 파일 경로 규칙은 대상이 디렉터리나 심볼릭 링크이면 선택한 파일을 바꾸기 전에 거부합니다. 이 타입 검사는 모든 반영 정책에 적용됩니다.

코덱 오류는 경로 규칙, 코덱, 실패 범주를 표시합니다. 진단 메시지, 디버그 출력, 조정 계획에는 원본 바이트와 렌더링된 바이트의 내용을 표시하지 않습니다.