Skip to Content

Layer

한 줄 소개: 보낸 문서가 어떻게 읽히는지 보이게 하는 서비스. PDF 를 올리면 공유 링크가 생기고, 받은 사람이 로그인 없이 웹 뷰어로 읽는 동안 페이지별 체류·완독률·리드 정보를 수집한다 (featpaper/DocSend 류, D-layer-1).

기술 스택

항목
언어Python 3.12
프레임워크FastAPI (async) + Jinja2 + vanilla JS (빌드 도구 없음)
DBPostgreSQL 16 + SQLAlchemy 2 async + Alembic
문서 처리 (v2, D-layer-4)HTML 네이티브 — nh3 sanitize + 헤딩 자동 섹션추출(htmlproc). PDF 래스터(PyMuPDF) 폐기, PDF 는 @media print 출력 기능만
디자인@axe/ui vendor v0.5.0 (build-time 번들) + 폰트 self-host (CDN 0)
배포docker compose blue/green + Caddy proxy (hive 패턴)
repogithub.com/axelabs-ai/layer (canonical /Users/axe/layer)

포트 (42xx)

포트용도
4200PostgreSQL 16 (layer-postgres)
4210app blue (active)
4211app green (passive)
4212axe-layer-proxy (Caddy, blue/green selector, 127.0.0.1 바인딩)

URL 컨벤션 — 듀얼 마운트 (라이브, D-layer-2)

B-platform-domain-scoping 의 2축 규칙을 그대로 적용. 같은 앱이 두 곳에 동시 노출 (gate 선례 미러링):

URL의미root_path
https://layer.axelabs.ai전역 서비스 웹사이트 (익명 랜딩 + 로그인)""
https://axe.axelabs.ai/layeraxe 내부 문서 회람/layer
  • cloudflared 가 path prefix 를 보존 → 앱이 scope["path"] 로 prefix 자체 감지 (MountPrefixMiddleware, 외부 헤더 비신뢰). Caddy 는 strip 안 함 (gate 패턴).
  • 공유 뷰어(무로그인): /v/{slug} · 페이지 이미지 /v/{slug}/p/{n}.jpg · 트래킹 /v/{slug}/events (각 마운트의 base 로 public_base(request) 가 절대 URL 생성).
  • 헬스: /health/ready (DB 포함) · /health/live
  • 로컬: http://127.0.0.1:4212/ (proxy) · blue :4210 / green :4211.

도메인 모델

v2 (HTML 네이티브, D-layer-4): usersdocumentsdocument_versions(링크 유지 버전 교체) → sections(헤딩 anchor + level + title). 공유 = links(slug, access_mode open/gated/allowlist × identify_policy email/phone/either/both, revoked_at) + link_allowlist(email/phone/domain). 추적 = visitors(email/phone 식별) → visits(visit_token, max_scroll_pct) → section_dwell(섹션별 ms 가산) + click_events. (구 pages/page_dwell 폐기 — alembic 0002)

엔게이지먼트 등급: 방문이 0.5초 이상 본 페이지 비율 기준 HOT ≥70% / WARM ≥50% / COLD 미만 (total 0.5s 미만 방문 제외 — featpaper 벤치마크 호환).

환경 변수 / 비밀

customers.yaml services.layer.secrets[] 등재 (vault 캡처 = 운영자 raw-bw 1회 필요):

envvault용도
LAYER_DB_PASSWORDlayer/axe/db-passwordpostgres
LAYER_SECRET_KEYlayer/axe/secret-key세션 서명·ip 해시
LAYER_ADMIN_PASSWORDlayer/axe/admin-password초기 admin 시드 (LAYER_ADMIN_EMAIL 과 페어)

비밀 아닌 설정: LAYER_DB_HOST/PORT/USER/NAME, LAYER_DATA_DIR(기본 /data 볼륨), LAYER_PUBLIC_BASE_URL(no-Host 폴백), LAYER_MOUNTED_PREFIX(기본 /layer — axe 마운트 prefix).

SSO (Microsoft Entra ID, D-layer-3)

envvault / 값용도
LAYER_OIDC_CLIENT_ID0e034eee-81a8-4e5f-a61b-1ff24ac7a5b0 (비밀 아님)Entra Layer Web
LAYER_OIDC_CLIENT_SECRETlayer/axe/oidc-client-secretconfidential client secret
LAYER_OIDC_TENANT_ID122fb574-… (비밀 아님)AXE 테넌트
LAYER_OIDC_ALLOWED_DOMAINaxellc.comemail 도메인 화이트리스트(authZ 경계)

세 OIDC 값이 모두 있을 때만 SSO 활성(oidc_enabled); 없으면 password 로그인만(로컬/테스트). 로그인 = /auth/sso/login → Entra → /auth/sso/callback. 자세한 검증/인가/흐름 보안은 auth.mdx “Layer Web app 설정”. 부트스트랩 운영자 계정은 sso: 타입, break-glass [email protected](password).

상태 (2026-06-13)

  • ✅ MVP — 업로드→링크→뷰어→추적→분석 핵심 루프 (B-layer-mvp), tests 41 green.
  • 라이브 노출layer.axelabs.ai(전역) + axe.axelabs.ai/layer(axe 회람) 듀얼 마운트, HTTPS e2e(로그인→대시보드, 양쪽 마운트 prefix·static·Set-Cookie 스코프·스푸핑 방어) 실측 (D-layer-2).
  • SSO 로그인 — Microsoft Entra ID OIDC(authorization code flow, FastAPI self-impl), 적대 리뷰 7건 반영, admin-consent 완료. 라이브 authorize redirect 양쪽 마운트 정확일치 실측, tests 63 green (D-layer-3). 브라우저 왕복은 사용자 실로그인 시 완결.
  • v2 HTML 네이티브 전환 (D-layer-4) — PDF 래스터 폐기 → HTML 반응형 뷰어(@axe/ui, 목차 스크롤스파이+읽음 dot, 햄버거) + sandbox iframe(allow-scripts, allow-same-origin 제외)+엄격 CSP(script-src nonce 1개)+nonce 추적→postMessage→부모 인증 POST. 이메일/전화 식별+allowlist, 섹션/스크롤 인게이지먼트(문서목록 행 노출). 3-wave 빌드(기초→라우터/UI→마무리), 적대 보안검토(data: URL XSS 차단)·실 Chrome e2e(추적 실행 검증), tests 114 green. 라이브 DB 0002 마이그레이션 + 컨테이너 재배포 + 클린 슬레이트 커토버 완료. 설계 = ~/layer/ctx/layer-v2-redesign-proposal.md, 계약 = ~/layer/CONTRACT_V2.md.
  • ⏳ 잔여 (B-layer-platform-integration): axe-cli SHIP_SERVICES 등록(현재 axe ship layer 불가 — 수동 docker compose --env-file .env.local up -d --build), vault 캡처 4건(DB/secret/admin + OIDC client secret — 현재 .env.local 에만, 운영자 raw-bw 1회).
  • Phase-2 defer: 멀티테넌트/과금, 영상·임베드 오버레이(Motion), 메일머지 링크, Zapier/플러그인, AI 요약·인용 최적화

함정

#함정증상회피
140xx/41xx 가 비어 보임 (CLAUDE.md 포트표 미갱신)신규 서비스 포트 충돌40xx=index, 41xx=gate 점유. 배정 전 lsof 실측 (layer=42xx)
2axelabs-docs repo 에 origin 원격 없음axe work docs <slug> 가 fetch 실패docs 는 CLAUDE.md 빠른 갱신 절차대로 공유 트리 직접 편집 + 본인 파일만 선택 커밋
Last updated on