수명주기와 HTTP API
모든 사용자 API는 Morit bearer session과 사용자 소유권 검사를 요구합니다. 앱은 repository 계층을 통해 호출하고 UI에서 URL을 직접 조합하지 않습니다.
설치 수명주기
package 수신
→ 크기·ZIP 경로·entry 검사
→ manifest·fragment compiled 결과 검사
→ Ed25519 signature와 publisher 확인
→ dependency bundle 확인
→ 임시 package 저장
→ Installation + default Instance + child dependency를 한 transaction으로 기록
→ package를 content-addressed store에 확정
→ catalog 새로고침
중간 단계가 실패하면 임시 파일과 DB 변경을 함께 rollback합니다. 목록 조회 실패를 설치 성공으로 바꾸거나, 부분 Installation이 남은 상태에서 duplicate 오류를 반환하지 않습니다.
한 Installation에는 설치와 함께 유지되는 default Instance가 있습니다. 여러 외부 계정은 이 Instance의 여러 Connection으로 관리합니다. 앱이 같은 package의 root Instance를 복제하지 않으며 dependency Child Instance는 Host가 내부에서 생성·정리합니다.
Marketplace와 설치
Raw install content type은 application/vnd.morit.plugin+zip, 최대 2 MiB입니다. Marketplace install과
파일 install은 같은 Host 설치 경로와 rollback 정책을 사용합니다.
조회와 설정
새 앱 흐름은 Installation 목록과 default Instance를 사용합니다. enabled, compatibility, granted
permission, required Connection, dependency가 모두 준비되어야 state가 active가 됩니다. 설정 변경은
generation을 갱신해 오래된 실행 결과가 새 상태를 덮지 못하게 합니다.
Connection과 OAuth
allow_multiple: true인 Credential은 Connection을 여러 개 만들 수 있습니다. OAuth callback은 state와
code를 확인한 뒤 Connection을 갱신하며 token을 URL·로그·응답에 반환하지 않습니다.
Capability 실행
실행 응답:
{
"plugin_id": "com.example.study",
"instance_id": "00000000-0000-4000-8000-000000000000",
"capability": "com.example.study.today",
"state": "completed",
"summary": "오늘 일정은 3개입니다.",
"data": {},
"evidence": [],
"error_message": null,
"error_code": null,
"retryable": false
}
Host는 실행 직전에 actor, owner, enabled, compatible version, generation, permission, Connection,
dependency, timeout을 다시 확인합니다. 실패는 error_code와 retryable을 구분하며 성공 summary로
숨기지 않습니다.
Host action outbox
앱은 action을 적용한 뒤 반드시 result를 전송합니다. lease 만료 시 중복 전달될 수 있으므로 notification key와 storage action은 idempotent하게 처리합니다.
Package, UI 이미지와 삭제
package 응답은 attachment Content-Disposition을 사용합니다. 삭제 시 purge_data와 manifest
data_policy를 적용하되 credential, OAuth state, pending Host action은 항상 별도 보안 정리를 거칩니다.
Storage 초기화는 actor가 소유한 정확한 Instance만 대상으로 하며 같은 Installation의 다른 Instance와
다른 사용자 데이터는 건드리지 않습니다. data_policy: retain은 uninstall/reinstall에만 적용되고
사용자가 명시적으로 초기화하면 즉시 삭제됩니다. 자세한 수명주기는
Plugin Local Storage와 AI 접근을 참고하세요.
Asset API는 활성화된 사용자 소유 설치가 UI Runtime v2에서 실제 참조한 이미지에만 접근할 수
있습니다. Host는 저장 package의 hash·signature·manifest를 다시 확인하고 확장자와 PNG/JPEG/GIF/WebP
magic byte, 실제 이미지 포맷을 검사합니다. package asset과 원격 이미지에 공통으로 가로·세로 각각
4096 px, 애니메이션 128 frame, width × height × frame 수 기준 frame 합산 16,000,000 pixel
한도를 적용합니다. 응답은 private cache, ETag, nosniff, same-origin resource policy를 사용하며
traversal·미참조 asset·다른 사용자의 설치는 노출하지 않습니다.
원격 이미지 API는 앱의 Host renderer가 호출하는 인증된 binary endpoint입니다. JSON body는
{"url":"https://..."}이고, URL은 image 또는 avatar의 capability 결과 binding에서
해석한 값이어야 합니다. 앱과 플러그인은 외부 URL을 직접 fetch하지 않습니다. 요청 actor가 Instance
owner여야 하고, package Manifest의 network 요청과 해당 활성 Instance의 network grant가 모두
유효해야 합니다.
Host는 공개 HTTPS만 허용하고 DNS/IP SSRF 검사를 거치며 redirect를 따르지 않습니다. upstream
응답은 PNG/JPEG/GIF/WebP MIME type, magic byte, 실제 이미지 포맷이 일치해야 합니다. 본문은 최대
512 KiB이고 위의 공통 dimension·animation 한도를 적용합니다. 성공 응답은 private, no-store,
Vary: Authorization, nosniff, same-origin resource policy를 사용합니다.
권한 부족은 403, 비활성화·generation/grant 변경은 409, unsafe URL·redirect는 422, 크기 초과는
413, 지원하지 않거나 손상된 이미지는 415, upstream 실패는 502, Host network 경계 미설정은
503으로 구분합니다. 소유하지 않은 Instance는 상세 정보를 노출하지 않습니다.
AI 생성/분석 artifact는 별도 소유권 경계인
GET /v1/ai/conversations/{conversation_id}/code-executions/{execution_id}/artifacts/{attachment_id}/access
에서만 signed URL을 발급합니다. 완료된 python_code, image_generation, image_edit 실행의 저장된
artifact manifest와 attachment 행이 모두 일치해야 하며 다른 대화·실행의 attachment ID는 404입니다.
앱 시작 복구
시작할 때 DB Installation과 content-addressed package store를 함께 점검합니다.
- DB가 없는 고아 임시/package 파일 정리
- DB가 가리키는 package가 없거나 hash가 다르면 명확한 load error
- 오래된 partial Connection/OAuth state 만료
- dependency Child Instance 재조정
- pending Host action lease 회수
손상된 metadata를 빈 목록으로 바꾸지 않습니다. 목록 없음, 인증 실패, 저장소 손상, network 실패를 각각 구분해 사용자에게 재시도 또는 복구 동작을 제공합니다.