WallpaperMachine
당신이 사랑하는 배경화면, 맥에서 새롭게 만나보세요
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Two products on one codebase and one sign-in: a private multi-family recipe vault with meal planning, a grocery list, AI recipe capture and an offline phone app, and a professional-kitchen dish pipeline. React, Expo and a moderation console over Supabase, served by one Cloudflare Worker; Postgres RLS is the security model.
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Recipes kept properly, for families and kitchens. Two products on one codebase, one design system and one sign-in:
recipes.enamelvault.com, plus a phone app): a private, multi-family
recipe vault. Families collect, organize, and pass down their recipes with photos, story, and
provenance. Each recipe carries a visibility flag (Private / Family / Public), and the public
ones surface in Potluck, an opt-in feed with a public page per cook.kitchen.enamelvault.com): dish development and sign-off for a
professional kitchen. A dish goes from development through trial at one site to the group
menu, every revision kept, one version signed off, and an owner can publish the approved
version to Potluck under the venue's name.They share no data: the kitchen's tables live in their own Postgres schema, and
supabase/kitchen_seal.test.ts fails if either side reaches into the other. A moderation
console on admin.enamelvault.com is the one sanctioned crossing, and even that reads the
kitchen counts-only through an edge function.
Live at https://enamelvault.com, which is the marketing surface and the sign-in chooser; the subdomains are the apps. All of them are served by the same Cloudflare Worker, which branches on hostname.
| Document | What it holds |
|---|---|
PRODUCT.md | Who it is for, the product decisions, the roadmap, and what is deliberately deferred. Not in this repo: it is gitignored, because the roadmap is not something a public repository needs to publish |
| DESIGN.md | The Enamel Vault design system: tokens, type, colour, components |
| docs/OPERATIONS.md | Running it locally, verifying, CI, deploying, secrets, monitoring, environment traps |
| docs/superpowers/specs/ | Per-feature design specs, newest first |
HANDOVER.md is local-only and gitignored: it holds current session state, nothing durable.
Engineering lessons are not kept in this repo as prose. A rule about specific code lives as a
comment at that code, where it cannot be missed; the generalisable half lives in the Obsidian
vault (Projects/FamilyRecipes/), which spans every project. See CLAUDE.md.
recipes. A pending edit to an
already-published recipe is a draft too: the recipe stays untouched until you publish, and if
someone else changed it meanwhile you are told and asked, rather than silently overwriting.An Expo / React Native client for the family app, in mobile/. It exists for the four things a
browser tab cannot do: work with no signal, live in the share sheet, ring with the screen off,
and reach the camera and microphone properly.
It is a client for the family app only: the kitchen app and the console stay web-only, and
supabase/kitchen_seal.test.ts holds that line for the phone app too.
It is noindex, enforced by a robots.txt and an X-Robots-Tag header rather than a meta tag
alone, since the crawlers that matter never run the page; worker/kitchen.ts carries the
reason, which changed on 2026-10-15 even though the rule did not. /professional on the bare
domain is therefore the kitchen's only indexable surface.
admin.enamelvault.com, in six sections: the report
queue, appeals, a Recipes section of counts and first-party numbers (who comes back, where
people fall out of onboarding, which features are used, which marketing page produced an
account), a People roster with suspend, take down, grant moderator and delete, a
Kitchen section that counts the other product and reports its throughput (dishes started
and signed off, the median days to sign-off, how many have been in development over a month,
and how many staff were active in the last seven days, as a count that never names anybody),
and a Trail of every moderator action with its author. Visible only to a moderator, and
the page re-checks that rather than relying on the nav hiding the link./terms, /privacy, /cookies, /help). The kitchen app has its own /terms and
/privacy, a different product with different parties, and its own acceptance gate. There is no cookie banner because the only cookie the app sets is the one that keeps you
signed in across its three hosts, which is strictly necessary and therefore exempt from
consent. src/lib/browserStorage.test.ts allows that one file by name and fails the build on
any other cookie, which enforces it rather than promising it. Things ARE counted, and saying otherwise here was wrong
until 2026-10-16: Cloudflare counts page views cookielessly, and a signed-in account's use
is recorded as one dated row per section per day so the project can tell whether anyone
comes back. Both are described on /privacy and /cookies, neither touches the device, and
that is why no banner is required.extract-recipe (AI recipe structuring), delete-account,
notify-report (emails the moderation queue through Resend), and admin (the moderator
console's backend, which needs the service-role key and so cannot live in the browser)./cooks/:handle, the four
public documents' prose rendered at build time and injected into #root, proxied preview
images and avatars, JSON-LD recipe data, /sitemap.xml, and two health routes: one for
the extract function, one reporting the commit the Worker was built from.Designed for portability: all data access is isolated in src/lib/api/, and nothing outside
it imports the Supabase client, so moving to a self-owned Node/Express + Postgres backend later
is a bounded swap.
This repo builds three web apps from one Worker: the family app (index.html, src/) on
recipes.enamelvault.com, the kitchen app (kitchen.html, src/kitchen/,
src/lib/api/kitchen/) on kitchen.enamelvault.com, and the moderation console
(admin.html, src/admin/) on admin.enamelvault.com, plus three static marketing pages on
the bare domain (see below). The phone app (mobile/) is a fourth client, built with Expo and
shipped through EAS rather than by the Worker. The three web apps share one sign-in, through a
cookie on .enamelvault.com. The family and kitchen apps share tokens, presentational
components and the auth plumbing, and share NO data: the kitchen's tables live in their own
Postgres schema and supabase/kitchen_seal.test.ts fails if either side reaches into the
other.
enamelvault.comThree static pages, served by the Worker from public/, listed in SITE_PAGES in
worker/site.ts. That map is the single source: the sitemap and the robots.txt disallow
lines are both generated from it, so a page cannot be announced without existing or exist
without being announced. Each .html filename is refused as a public path, so every page has
exactly one URL and that URL is its canonical.
| URL | File | What it is |
|---|---|---|
/ | public/site.html | The hub. Belongs to neither product; links to both pages and carries the rack of public recipes |
/family | public/family.html | The family app's case, and its sign-up |
/professional | public/professional.html | The kitchen app's case, and its sign-up. This is the kitchen's ONLY indexable surface, because kitchen.enamelvault.com is noindex |
They share public/site.css. Anything else they reference must be in isSiteAsset(), or the
bare host redirects it to the app host, which makes it cross-origin and the CSP refuses it
silently; src/site.assets.test.ts is the guard, written after exactly that shipped.
Four tests run over all three pages from a list: site.a11y (structure, distinct titles and
canonicals, no inline script, hub neutrality), site.assets, site.tokens (palette drift)
and site.claims (no claim the kitchen terms disclaim). Adding a page means adding it to
those lists, or it ships unchecked.
Docker must be running.
npx supabase start
npm run dev
Full setup, environment variables and the verify commands are in docs/OPERATIONS.md.
Shipped, newest first: the phone app (an offline shelf and recipe, editing with no signal, a conflict screen, and Cook Mode with timers that ring locked) · one sign-in across all three hosts, and the moderation console moved to its own host with its own door · an audit trail naming the author of every moderator action · Cook Mode reading the step aloud · the public documents and the WCAG AA contrast and labelling fixes · the kitchen app (the dish pipeline, Numbers, its own host and its own Postgres schema) · search-engine indexing (robots, sitemap, canonical, JSON-LD) · moderation, reporting and takedown · save and fork with attribution · Potluck, the public feed (browse, search, follow) · public identity and public cook pages · optional ingredients and alternatives · week-grid drag and drop · error and uptime monitoring · verified email and Google sign-in · the cupboard and "cook now" · ingredient aisles · My Kitchen week grid · recipe enrichment and grocery scaling · My Kitchen meal planning · v1 private multi-family vault.
Next: opening Potluck beyond signed-in users, which moderation and the public documents now unblock, and then costs for the kitchen (yield, portion cost, supplier, allergens) in their own sidecar table.
Later: LLM / entity canonicalization for ingredients, and a family-editable ingredient catalog. Per-recipe link previews are already built, in the Worker.
PRODUCT.md has the detail, including what is deferred and why. It is deliberately not published here.