Pasty 사용 안내
팀에서 직접 운영하는 가벼운 전달용 게시판. 텍스트나 파일을 올리면 6자리 공유 코드가 나오고, 코드만으로 받아가며, 기한이 지나면 자동 삭제됩니다.
23456789ABCDEFGHJKMNPQRSTUVWXYZ. 헷갈리는 0/O/1/I/L 은 빼서 소리 내어 전해도 틀리지 않습니다. 입력할 때 대소문자는 가리지 않습니다.1무엇인가
| 항목 | 사실 |
|---|---|
| HTML 페이지 | AI 에이전트용: 토큰으로 HTML 페이지 하나를 호스팅하고, 브라우저에서 바로 열리는 /p/ URL을 받습니다(§9 참고) |
| 로그인 | 없습니다. 만들 때도 볼 때도 인증하지 않으며, 코드를 가진 사람은 읽을 수 있습니다 |
| 내용 | 텍스트 / 파일 하나 / 텍스트 + 파일 |
| 텍스트 상한 | 50,000자 (JSON 엔드포인트는 본문 100KB 제한이 따로 있습니다 · §4.2) |
| 파일 상한 | 100 MB, 한 개, 형식 제한 없음 |
| 만료 | 5분 / 30분 / 1시간 / 1일 / 7일, 기본값은 1일 |
| 삭제 / 수정 / 목록 | 모두 없습니다. 만든 뒤에는 되돌릴 수 없고 만료를 기다려야 합니다 |
| 저장 | 메타데이터와 텍스트는 Redis(TTL 적용), 파일은 Cloudflare R2 |
맞는 용도: 내 기기들 사이에서 텍스트나 파일 옮기기, 눈앞의 상대에게 코드를 불러 주기, Pod·서버의 산출물을 원격 사용자에게 전달하기, 고객에게 단기 유효한 다운로드 링크 주기.
맞지 않는 용도: 회수해야 할 내용, 오래 보관해야 할 내용, 키·자격 증명·개인정보(§7 참고).
2웹에서 쓰기
2.1 만들기
pasty.youlidao.ai 를 열면 기본이 「만들기」 탭입니다.
- 텍스트: 입력창에 쓰거나 붙여넣습니다. 오른쪽 아래에
사용 / 50,000가 표시되고, 넘으면 빨갛게 바뀌며 버튼이 비활성화됩니다. - 파일(선택): 점선 영역에 끌어다 놓거나, 눌러서 고르거나, 텍스트 입력창에 포커스가 없을 때
⌘V(윈도우는Ctrl+V)로 클립보드의 이미지를 붙여넣습니다. 파일은 하나만 되며 여러 개를 놓으면 첫 개만 쓰고 그렇게 알려 줍니다. 이미지는 미리보기, 그 밖은 아이콘·이름·크기·MIME 을 보여 주고 × 로 뺍니다. - 만료 시간: 드롭다운, 기본값은 1일.
- 「만들기」를 누릅니다. 파일이 있으면 버튼이 진행률로 바뀝니다. 텍스트가 50,000자를 넘거나 파일이 100MB 를 넘으면 요청을 보내기 전에 브라우저에서 막습니다.
- 결과 화면: 큼직한 공유 코드, 만료 안내, 코드 복사, 새로 만들기.
2.2 받아가기
- 코드로: 첫 화면에서 「가져오기」 탭으로 바꾸고 6자를 입력합니다. 입력창은 자동으로 대문자로 바꾸고 집합에 없는 문자는 버리는데, 조용히 사라지게 두지 않고 무엇을 무시했는지 알려 줍니다. 6자가 채워지면 0.5초 뒤 자동으로 조회해 이동하며, 버튼을 누르면 즉시 조회합니다. 없는 코드면 입력창을 비우고 알려 줍니다.
- 링크로:
https://pasty.youlidao.ai/{CODE}, 대소문자는 가리지 않습니다.
2.3 보기 화면의 구성
오른쪽 위에 남은 시간(「4분 후 만료」「3시간 후 만료」「6일 후 만료」)이 표시됩니다. 그 아래는 내용 종류에 따라:
| 내용 | 표시 |
|---|---|
| 텍스트 | 고정폭 <pre> 로 그대로(줄바꿈과 들여쓰기 보존), 아래에 복사 버튼 |
| 이미지 | png / jpeg / gif / webp / svg 는 화면 안에서 바로 보이고, 누르면 원본이 새 탭에서 열립니다. 아래에 이름과 크기 |
| 브라우저 기본 뷰어를 끼워 넣고 옆에 「새 탭에서 열기」와 「내려받기」. iOS Safari 는 끼워 넣은 틀에 첫 쪽만 보여 줍니다 — 「새 탭에서 열기」를 쓰세요 | |
| 그 밖 | zip, Word, Excel, 영상 등: 파일 카드(아이콘·이름·크기·MIME) + 내려받기 버튼 |
| 텍스트 + 파일 | 텍스트 먼저, 첨부 구분선, 그다음 파일 |
10초 안에 응답이 없으면 시간 초과를 알립니다.
2.4 남에게 넘기는 세 가지 방법
| 형태 | 예 | 쓰기 좋은 상황 |
|---|---|---|
| 코드만 | MN3W2R | 말로, 손으로, 전화로. 상대가 첫 화면에서 입력합니다 |
| 페이지 링크 | https://pasty.youlidao.ai/MN3W2R | 메신저나 메일. 열면 전체 화면이 나옵니다 |
| 파일 직접 링크 | https://pasty.youlidao.ai/api/pastes/MN3W2R/file | 이미지와 PDF 는 바로 그려지고 나머지는 내려받아집니다. <img> 나 wget 에 넣기 좋습니다 |
3명령줄 / 스크립트에서 쓰기
3.1 텍스트 보내기
curl -s -X POST https://pasty.youlidao.ai/api/pastes \
-H 'Content-Type: application/json' \
-d '{"content":"공유할 텍스트","expiresIn":"1h"}'
# → {"code":"7FZEN4","expiresAt":"2026-09-09T16:56:23.246Z"}
내용이 파일에서 올 때는 손으로 이스케이프하지 말고 jq 로 JSON 을 만드세요:
jq -n --rawfile c notes.md '{content:$c, expiresIn:"1d"}' \
| curl -s -X POST https://pasty.youlidao.ai/api/pastes \
-H 'Content-Type: application/json' --data @-
3.2 파일 보내기(설명도 함께)
curl -s -m 120 -X POST https://pasty.youlidao.ai/api/pastes \
-F "file=@/path/보고서.pdf" \
-F "content=이게 무엇인지 한 줄로" \
-F "expiresIn=1h"
# → {"code":"KTCK33","expiresAt":"…","hasFile":true}
- 파일이 여럿이면 먼저
zip -r로 하나로 묶으세요. 파일 엔드포인트는 IP 당 분당 5회이므로 반복문으로 하나씩 올리지 마세요. type=을 지정할 필요 없습니다. 서버가 파일 내용으로 MIME 을 판정합니다(§4.4).- 파일 이름은 한글을 포함한 유니코드를 그대로 유지합니다.
3.3 가져오기
# 메타데이터 + 텍스트
curl -s https://pasty.youlidao.ai/api/pastes/KTCK33
# 텍스트만
curl -s https://pasty.youlidao.ai/api/pastes/KTCK33 | jq -r .content
# 파일 내려받기 (-J 는 서버가 준 이름, -O 는 저장)
curl -s -O -J https://pasty.youlidao.ai/api/pastes/KTCK33/file
/raw 엔드포인트는 없습니다. GET /{CODE} 가 돌려주는 것은 프런트엔드의 HTML 입니다. 스크립트에서는 항상 /api/pastes/{CODE} 를 호출해 .content 를 쓰세요.
3.4 기기 사이에 파일을 옮길 때 권장 절차
# 보내는 쪽
shasum -a 256 FILE
curl -s -X POST https://pasty.youlidao.ai/api/pastes -F "file=@FILE" -F "expiresIn=30m"
# 받는 쪽
curl -s -O -J https://pasty.youlidao.ai/api/pastes/CODE/file
shasum -a 256 FILE # 보내는 쪽과 비교
전송 자체는 무손실입니다(zip / png 왕복에서 바이트 일치 실측). 체크섬은 코드를 잘못 적거나 내려받다 끊긴 경우를 잡기 위한 것입니다.
JSON 텍스트밖에 보낼 수 없는 환경(오래된 스크립트, 제한된 샌드박스)이라면 base64 로 우회할 수 있습니다. base64 -i FILE | tr -d '\n' 을 content 로 보내고 받는 쪽에서 base64 -d 합니다. base64 는 용량이 ×1.33 이 되고 100KB 본문 제한도 받으므로 작은 파일에만 맞습니다.
4API 레퍼런스
4.1 엔드포인트 개요
| 메서드 | 경로 | 용도 | 속도 제한(IP 기준) |
|---|---|---|---|
GET | /api/health | 상태 확인 | 없음 |
POST | /api/pastes | application/json = 텍스트만, multipart/form-data = 파일 ± 텍스트 | JSON 분당 10회, multipart 분당 5회. 카운터는 각각 독립 |
GET | /api/pastes/:code | 메타데이터 + 텍스트 가져오기 | 없음 |
GET | /api/pastes/:code/file | 파일 스트리밍 내려받기 / 미리보기 | 없음 |
POST | /api/pages | HTML 페이지 하나 호스팅(토큰 필요): JSON html 또는 multipart file | 분당 10회 |
GET | /p/:code | 호스팅된 페이지를 샌드박스 CSP로 표시 | 없음 |
GET | /p/:code/pdf | 호스팅된 페이지를 A4 PDF로 내보내기(첫 렌더링 후 캐시) | 분당 10회 |
생성 응답에는 항상 속도 제한 헤더가 붙습니다: ratelimit-limit, ratelimit-remaining, ratelimit-reset(초), ratelimit-policy(예 10;w=60). 초과하면 429 를 돌려주고 code 는 RATE_LIMITED(텍스트) 또는 FILE_RATE_LIMITED(파일)입니다.
4.2 POST /api/pastes · JSON
{ "content": "필수. 앞뒤 공백을 뺀 뒤 비어 있지 않고 50,000자 이하",
"expiresIn": "5m | 30m | 1h | 1d | 7d (생략 가능, 기본 1d)" }
// 201
{ "code": "7FZEN4", "expiresAt": "2026-09-09T16:56:23.246Z" }
요청 본문 상한은 100KB. Express 의 JSON 파서 제한이라 글자 수 검사보다 먼저 걸립니다. 한중일 문자 40,000자는 약 120KB 라서 413 request entity too large 가 납니다. 긴 글은 약 33,000자 안으로 줄이거나, 이 제한이 없고 50,000자를 다 쓸 수 있는 multipart 의 content 필드로 바꾸세요.
검증 실패의 형태(details 는 JSON 경로에만 붙습니다):
{ "error": "Content must not be empty",
"code": "CONTENT_EMPTY",
"details": [ { "path": "content", "message": "CONTENT_EMPTY" } ] }
4.3 POST /api/pastes · multipart
| 필드 | 필수 | 규칙 |
|---|---|---|
file | content 와 둘 중 하나 | 파일 하나 100MB 이하(104,857,600 바이트), 형식 제한 없음 |
content | file 과 둘 중 하나 | 앞뒤 공백을 뺀 뒤 비어 있지 않고 50,000자 이하 |
expiresIn | 아니오 | 위와 같음, 기본 1d |
201 응답은 { "code", "expiresAt", "hasFile": true } 이며 파일이 없으면 hasFile 이 없습니다. 처리 순서는 R2 가 먼저이고 성공해야 Redis 에 씁니다. R2 가 실패하면 아무것도 남지 않고, Redis 가 실패하면 R2 객체를 지워 되돌립니다.
파일 이름 규칙:
- 255자 초과 → 400
FILENAME_TOO_LONG ../,..\또는 그 URL 인코딩 형태 포함 → 400FILENAME_INVALID, 보안 로그에 기록/ \ : * ? " < > |는_로 바꾸고 앞쪽 점과 공백은 제거- 이름이 비면 MIME 에 따라
unnamed.png같은 이름을 붙임 - 한글 등 유니코드는 그대로 유지(예전에는 latin1 로 깨졌습니다)
4.4 MIME 판정 — 미리보기냐 내려받기냐를 정함
서버는 클라이언트가 알려 준 Content-Type 을 그대로 믿지 않고 다음 순서로 정합니다:
- 파일의 매직 넘버(이미지, PDF, zip, Office, 오디오·비디오 등 이진 형식)
- 클라이언트의 선언. 단 비어 있지 않고
application/octet-stream이 아닐 때만(매직 넘버가 없는 txt / csv / json / svg 를 잡습니다) - 확장자
application/octet-stream
그래서 curl -F file=@x.zip 은 application/zip 으로 저장되고, 확장자도 선언도 없는 PDF 도 PDF 로 알아보고 미리보기가 되며, PDF 를 .txt 로 바꿔 올려도 PDF 로 다룹니다.
4.5 GET /api/pastes/:code
코드는 정확히 6자이고 모두 집합 안의 문자여야 하며, 아니면 400 입니다. 서버에서 대문자로 맞춥니다.
{
"content": "설명 문구(파일만 있을 때는 빈 문자열)",
"type": "text | file | mixed",
"fileMetadata": {
"fileName": "스크린샷.png", "fileSize": 74, "mimeType": "image/png",
"storageKey": "pastes/U9RBRW/스크린샷.png"
},
"createdAt": "2026-09-09T17:00:41.045Z",
"expiresAt": "2026-09-09T17:05:41.045Z"
}
type 과 fileMetadata 는 파일이 있을 때만 나옵니다. 초기의 텍스트 전용 기록에는 type 자체가 없을 수 있으며 그때는 text 로 다루세요.
4.6 GET /api/pastes/:code/file
파일 본체를 스트림으로 돌려주며 메모리에 담아 두지 않습니다. 응답 헤더:
| 헤더 | 값 |
|---|---|
Content-Type | 판정된 MIME |
Content-Disposition | image/* → inline, PDF → inline; filename=…, 그 밖 → attachment; filename=…(RFC 5987 인코딩이라 비 ASCII 이름도 안전) |
Content-Length | 파일 크기 |
Content-Security-Policy | 인라인 응답(이미지·PDF)은 사이트 기준에 sandbox 를 더해 스크립트가 절대 실행되지 않습니다. 첨부 응답은 기준 그대로입니다 |
4.7 오류 코드 표
모든 오류 응답은 { "error": <영문 설명>, "code": <식별자> } 입니다. 프로그램에서는 code 로 분기하세요. 이쪽이 안정된 계약이고 error 는 사람이 읽는 영문이라 표현이 바뀔 수 있습니다. 웹 화면은 error 를 보여 주지 않고 code 로 자체 사전을 찾아 방문자의 언어로 표시합니다.
| 상태 | code | 발생 상황 |
|---|---|---|
| 400 | CONTENT_EMPTY | content 가 비었거나 공백뿐 |
| 400 | INVALID_EXPIRY | expiresIn 이 5m/30m/1h/1d/7d 가 아님 |
| 400 | INVALID_CODE_FORMAT | 공유 코드가 6자의 올바른 문자가 아님 |
| 400 | NO_CONTENT_PROVIDED | multipart 에 content 도 file 도 없음 |
| 400 | FILENAME_TOO_LONG / FILENAME_INVALID | 파일 이름이 255자 초과 / 경로 탈출 포함 |
| 400 | VALIDATION_ERROR | multipart 가 손상됨, 또는 그 밖의 분류되지 않은 검증 실패 |
| 400 | PP_SLUG_INVALID | 슬러그 형식·길이가 올바르지 않거나 예약된 경로임 |
| 401 | PAGE_AUTH_REQUIRED | 토큰 없이, 또는 잘못된 토큰으로 페이지를 만들려 함 |
| 401 | PP_AUTH_REQUIRED | 토큰 없이, 또는 잘못된 토큰으로 영구 페이지를 생성함 |
| 404 | PASTE_NOT_FOUND | 코드가 없거나 만료됨 |
| 404 | NO_FILE_IN_PASTE | 그 코드는 텍스트뿐이고 파일이 없음 |
| 404 | FILE_NOT_FOUND | R2 객체가 이미 정리됨 |
| 413 | CONTENT_TOO_LARGE | 텍스트가 50,000자 초과, 또는 JSON 본문이 100KB 초과 |
| 413 | FILE_TOO_LARGE | 파일이 100MB 초과 |
| 413 | PAGE_TOO_LARGE | HTML이 5MB 초과 |
| 409 | PP_SLUG_TAKEN | 이미 다른 영구 페이지나 사용 중인 공유 코드가 그 슬러그를 쓰고 있음 |
| 429 | RATE_LIMITED / FILE_RATE_LIMITED | 텍스트 생성 / 파일 업로드 제한. 카운터는 각각 독립 |
| 501 | FILE_UPLOAD_DISABLED | R2 가 설정되지 않은 서버에 파일이 도착함 |
| 501 | PAGE_HOSTING_DISABLED | 서버에 PAGE_API_TOKENS가 설정되지 않아 페이지 호스팅이 꺼져 있음 |
| 501 | PP_HOSTING_DISABLED | 서버에 PP_API_TOKENS가 설정되지 않아 영구 페이지 기능이 꺼져 있음 |
| 503 | SERVICE_UNAVAILABLE / SERVICE_BUSY | Redis 장애 / 공유 코드 충돌이 5회 연속 |
| 503 | STORAGE_UNAVAILABLE / FILE_UPLOAD_FAILED | R2 사용 불가 / 업로드 실패 |
| 503 | PDF_EXPORT_UNAVAILABLE | PDF 렌더링 실패, 대기열 가득 참, 또는 서버에 Chromium이 설정되지 않음 |
| 500 | INTERNAL | 처리되지 않은 서버 예외 |
5Claude Code / cc-hub 연동
youlidao.ai 의 cc-hub 세션과 빌드 센터 Pod 에는 pasty-share skill(v3)이 들어 있습니다. 「이거 나한테 보내 줘」「이 파일 고객에게」라고 AI 에게 말하면:
- multipart 로 원본 파일 그대로 올립니다. skill 의 철칙으로, PDF 나 Word 를 markdown 으로 바꿔 보내는 것은 금지되어 있습니다
- 기본값은
expiresIn=1h. 파일이 여럿이면 먼저 zip 으로 묶습니다 - 공유 코드와 보기 링크를 답하고, 이미지와 PDF 에는 /file 직접 링크도 덧붙입니다
- 매번 만료 시간과 「코드를 가진 사람은 누구나 볼 수 있음」을 분명히 말합니다
skill 의 금지선: .env, 개인 키, 토큰, 자격 증명, 데이터베이스 덤프는 올리지 않습니다. 민감할 수 있으면 먼저 사용자에게 묻고, 그래도 올린다면 5m / 30m 을 씁니다.
6데이터 수명 주기
| 단계 | 일어나는 일 |
|---|---|
| 생성 | Redis 에 paste:{CODE} 를 선택한 만료 시간의 TTL 로 씁니다. 파일이 있으면 먼저 R2 에 pastes/{CODE}/{이름} |
| 유효 기간 중 | 횟수 제한 없이 읽을 수 있고 세지도 않습니다 |
| 만료 | Redis 키가 스스로 사라지고 조회는 404 가 됩니다 |
| 만료 후 정리 | 서버가 한 시간마다 훑어 사라진 기록에 해당하는 R2 객체를 지웁니다. R2 자체의 수명 주기 규칙이 보조로 동작합니다(문서상 8일) |
| 로그 | 구조화 로그에는 IP, 크기, MIME, 정리된 파일 이름만 남기고 본문은 기록하지 않습니다 |
공유 코드는 CSPRNG 에 거부 샘플링을 적용해 고르게 생성합니다. 31의 6제곱 = 887,503,681가지이며, 충돌하면 최대 5회까지 다시 뽑습니다.
7보안 경계 — 쓰기 전에 반드시 읽을 것
- 인증이 없습니다: 코드가 유일한 열쇠이고, 코드나 링크를 가진 사람은 누구나 읽습니다. 링크를 단체방에 올리면 전원이 읽을 수 있습니다.
- 되돌릴 수 없습니다: 잘못 보냈다면 만료를 기다리는 수밖에 없습니다. 망설여지면 5분이나 30분으로 하세요.
- 올리면 안 되는 것: 비밀번호, API 키, 개인 키, 인증서, .env, 개인정보가 담긴 데이터.
- 올린 내용은 실행되지 않습니다: 이미지와 PDF 의 인라인 응답에는 sandbox CSP 가 붙어, 스크립트가 든 SVG 를 올려도 동작하지 않습니다(실제 브라우저로 확인). HTML, zip 등은 항상 첨부로 내려받아지며 그려지지 않습니다.
- 다른 사이트가 iframe 으로 끼워 넣을 수 없습니다(
frame-ancestors 'self'). - 내용 페이지는 검색 엔진에 실리지 않습니다: robots.txt 가 공유 코드 경로의 크롤링을 막고 응답에도
X-Robots-Tag: noindex가 붙습니다. 크롤러를 막는 것이지 코드를 가진 사람을 막는 것은 아닙니다. - 호스팅된 HTML 페이지(§9)는
/p/{CODE}에서만 표시되며 전용 샌드박스 CSP가 붙습니다: 별도 출처, API 호출 불가, 양식 제출 불가. 만들려면 토큰이 필요합니다.
8자주 묻는 질문
- 브라우저에 ERR_CONNECTION_CLOSED 가 뜨거나 curl 이 000 을 돌려줍니다
- 중국 본토에서 직접 연결할 때 이따금 생깁니다. 새로 고치거나 한 번 다시 시도하면 됩니다. 상태 확인과 읽기 엔드포인트 자체는 안정적입니다.
- 만들기 버튼이 계속 회색입니다
- 텍스트가 비었고 파일도 고르지 않았거나, 텍스트가 50,000자를 넘었습니다(카운터가 빨개집니다).
- 휴대폰에서 PDF 의 첫 쪽만 보입니다
- iOS Safari 끼워 넣기 틀의 제약입니다. 「새 탭에서 열기」를 쓰세요.
- 한글 파일 이름이 깨져서 내려받아집니다
- 2026-09-10 이전에 올린 기록만 그렇습니다(그때는 서버가 이름을 latin1 로 해석했습니다). 이후 올린 것은 고쳐졌고, 오래된 기록은 만료로 사라집니다.
- curl 로 올린 zip 이 예전에는 application/octet-stream 이었습니다
- curl 이 .zip 을 몰라서 octet-stream 이라고 선언했고 예전 코드가 그대로 믿었습니다. 이제는 파일 내용으로 판정하므로
;type=이 필요 없습니다. - 내용을 고치거나 지우거나 기한을 늘릴 수 있나요
- 없습니다. 그런 엔드포인트가 없으니 새로 만드세요.
- 429 가 나오면 어떻게 하나요
- 응답 헤더의
ratelimit-reset(초)만큼 기다리세요. 텍스트는 분당 10회, 파일은 분당 5회이고 IP 기준입니다. 파일이 여럿이면 zip 하나로 묶으세요. - 긴 글이 413 request entity too large 로 안 보내집니다
- JSON 본문 상한이 100KB 입니다. multipart 의
content필드를 쓰거나 나눠 보내세요. - 화면 언어를 바꿀 수 있나요
- 있습니다. 바닥글에 언어 선택(简体中文 / English / 日本語 / 한국어)이 있고 선택은 기기에 저장됩니다.
?lang=ko처럼 직접 지정할 수도 있습니다.
9HTML 페이지 호스팅: AI 에이전트용 임시 웹 페이지
9.1 무엇을 위한 기능인가
AI 에이전트가 보고서, 대시보드, 프로토타입을 만들었을 때 사람에게 건네야 하는 것은 내려받을 파일이 아니라 열면 바로 보이는 URL입니다. HTML 문서 하나를 POST하면 https://pasty.youlidao.ai/p/{CODE}를 돌려받고, 만료되면 자동으로 삭제됩니다.
| 항목 | 내용 |
|---|---|
| 만들 수 있는 사람 | 토큰 보유자: 헤더 Authorization: Bearer <token>. 서버에 토큰이 설정되어 있지 않으면 501을 반환하며 기능이 꺼져 있습니다 |
| 볼 수 있는 사람 | 다른 콘텐츠와 마찬가지로 코드나 링크가 있으면 누구나. 로그인 불필요 |
| 크기 | HTML 문서 하나당 5 MB 이하 |
| 만료 | 5m / 30m / 1h / 1d / 7d, 기본값 1일 |
| 공유 코드 | 텍스트·파일과 같은 코드 공간을 씁니다. /{CODE}는 페이지 카드를, /p/{CODE}는 페이지 자체를 보여 줍니다 |
| 요청 제한 | IP당 분당 10회 |
9.2 페이지 만들기
curl -s -X POST https://pasty.youlidao.ai/api/pages \
-H "Authorization: Bearer $PASTY_PAGE_TOKEN" \
-F "file=@report.html" \
-F "expiresIn=7d"
# → {"code":"MN3W2R","expiresAt":"…","url":"https://pasty.youlidao.ai/p/MN3W2R"}
HTML이 이미 변수에 있다면 JSON으로 보냅니다. 필드 이름은 html입니다:
jq -n --rawfile h report.html '{html:$h, expiresIn:"1d"}' \
| curl -s -X POST https://pasty.youlidao.ai/api/pages \
-H "Authorization: Bearer $PASTY_PAGE_TOKEN" \
-H 'Content-Type: application/json' --data @-
- 201 응답:
{ "code", "expiresAt", "url" }. 사람에게 보낼 링크는url입니다. - JSON 요청 본문은 이스케이프 여유분을 고려해 페이지 한도의 2배까지 받습니다. 5MB에 가까운 페이지는 multipart를 쓰세요.
- 토큰은 환경 변수에 두고, 페이지·로그·채팅에는 절대 쓰지 마세요.
9.3 페이지를 열었을 때 할 수 있는 것
/p/{CODE}는 text/html; charset=utf-8로, 전용 CSP와 함께 반환됩니다:
| 가능 | 불가능 |
|---|---|
| 인라인 스크립트, 그리고 다음 CDN의 스크립트: cdnjs.cloudflare.com, cdn.jsdelivr.net, unpkg.com, cdn.tailwindcss.com | 네트워크 호출: fetch / XHR / WebSocket 모두 차단(connect-src 'none') |
| 인라인 스타일, Google Fonts, data: 글꼴 | 양식 제출(form-action 'none') |
| https / data: / blob: 출처의 이미지·오디오·동영상 | iframe, object 삽입 |
| 팝업, 외부 링크를 새 탭에서 열기 | localStorage / 쿠키: 페이지는 독립된 샌드박스(별도 출처)에서 실행되므로 접근하면 예외가 발생합니다 |
샌드박스 덕분에 페이지의 스크립트는 pasty 자체로 행세할 수 없고, 이 사이트에 저장된 어떤 것도 읽을 수 없습니다. 외부 전송 차단은 페이지가 피싱 양식으로 쓰이지 않게 하기 위한 것이지만 완벽하지는 않습니다(이미지 URL이나 페이지 이동으로 데이터를 내보낼 수 있음). 진짜 관문은 만들 때 필요한 토큰입니다.
코드가 없거나, 만료되었거나, 페이지가 아니라 텍스트·파일이면 /{CODE}로 302 리디렉션되어 일반 보기 페이지에서 상황을 안내합니다. 익명 파일 업로드로 올라온 .html은 항상 다운로드로만 제공되며 /p/에서 표시되지 않습니다.
9.4 페이지를 작성하는 AI용 체크리스트
- 파일 하나로 완결: CSS와 JS는 인라인, 이미지는 data: URI 또는 https 링크, 라이브러리는 cdnjs.cloudflare.com, cdn.jsdelivr.net, unpkg.com, cdn.tailwindcss.com에서 버전을 고정해 불러옵니다.
- 네트워크 요청에 의존하지 않기: JSON을 fetch하지 말고 데이터를 페이지에 직접 넣습니다.
- localStorage 쓰지 않기. 꼭 써야 한다면 try/catch로 감싸고, 없어도 페이지가 보이게 합니다.
<meta charset="utf-8">와 viewport를 넣어 휴대폰에서도 읽히게 합니다. 중국 본토에서는 Google Fonts가 불안정하므로 모든 글꼴에 시스템 글꼴 대체를 지정합니다.- 만료는 기본
1d, 사람에게 보여 줄 보고서라면7d. 답장에는url과 만료 시각을 쓰고, 링크가 있으면 누구나 볼 수 있다고 알립니다. - 비밀 키, 개인정보, 내부 자격 증명은 넣지 않습니다. 규칙은 §7과 같습니다.
기계가 읽을 전체 설명은 /llms.txt에 있습니다.
9.5 PDF로 내보내기
호스팅된 페이지마다 오른쪽 아래에 Export PDF 버튼이 있습니다(브라우저 언어가 중국어인 독자에게는 중국어로 표시). 링크는 /p/{CODE}/pdf이며, 이 주소를 그대로 공유해도 됩니다.
- 서버가 같은
/p/{CODE}를 같은 샌드박스 규칙으로 Chromium에서 열어 A4 PDF로 만듭니다. 배경색은 유지하고, 항상 라이트 모드로 출력하며, 버튼 자체는 PDF에 넣지 않습니다. - 첫 내보내기에는 몇 초에서 15초 정도 걸립니다. 결과는 캐시되어 이후 요청에는 바로 반환됩니다. PDF는 페이지와 함께 만료되고 함께 정리됩니다.
- 파일 이름과 함께
inline으로 반환하므로 휴대폰과 앱 내 브라우저에서는 바로 미리 보고, 컴퓨터에서는 저장할 수 있습니다. - 요청 제한은 IP당 분당 10회입니다. 렌더링에 실패하면 503
PDF_EXPORT_UNAVAILABLE을 반환하니 다시 시도하세요. - 잘 내보내려면 마우스를 올리거나 클릭해야만 보이는 내용을 피하고, 넓은 표는 가로 스크롤 컨테이너에 넣으세요. PDF에서는 A4 폭에 맞춰 줄바꿈됩니다.