Deploy the knowledge base

Publishes this vault to https://knowledge.villamarket.ai.

cd ~/stacks/villa-knowledge-base
AWS_PROFILE=villaai ./infra/publish.sh

That builds with Quartz, mirrors pages into directory form, syncs to S3 and waits on a CloudFront invalidation. Takes about a minute.

Resources (villaai / 914499832220)

Value
Bucketvilla-knowledge-base-914499832220 (ap-southeast-1, private)
CloudFrontE3Q9JXUDOEHPYBdoy633tttpsgg.cloudfront.net
Certificateus-east-1 ACM for knowledge.villamarket.ai
DNSA + AAAA alias in zone Z069245539VG1ZOH980UK
Origin accessOAC E1YFMIXFTV8946; bucket policy allows only this distribution
URL rewriteCloudFront function villa-kb-pretty-urls (viewer-request)

The bucket blocks all public access. CloudFront reads it through Origin Access Control, so the S3 URL is not browsable directly — that is intended.

Why pages are uploaded twice

Quartz emits flat files (systems/calculatecost2.html) but links to them without the extension (/systems/calculatecost2). S3 only answers for exact keys, so an extensionless request 404s.

The CloudFront function rewrites any extensionless path to …/index.html, and publish.sh mirrors each page into that directory form. Both halves are required — change one and every sub-page 404s.

Paths that still contain a dot in the last segment (.css, .js, .webp) pass through untouched.

Adding content

Add a markdown file under the right folder in content/ with the frontmatter described in AGENTS.md, then run the publish script. No registration step — Quartz discovers files, and the folder listings, tag pages, graph and search index all rebuild.

Before publishing, check that every wikilink resolves. A typo renders as plain text rather than erroring:

python3 - <<'PY'
import re, pathlib
notes = {p.stem for p in pathlib.Path('content').rglob('*.md')}
for p in pathlib.Path('content').rglob('*.md'):
    for m in re.finditer(r'\[\[([^\]|#]+)', p.read_text()):
        if m.group(1).strip() not in notes:
            print('broken:', m.group(1).strip(), '<-', p)
PY

Local preview

npx quartz build --serve      # http://localhost:8080

Not yet automated

Deployment is manual on purpose for now — wiring GitHub Actions needs an OIDC role in the villaai account, and a workflow that fails on every push for want of credentials is worse than no workflow. When someone adds that role, infra/publish.sh is the only command the job needs to run.