The Kai Project is an open-source platform for Japanese graduate school entrance exam archives, public solutions, and preparation experience. Signed-in users can also track progress, take notes, and manage private study data stored directly in the project database.
"Answer to the Ultimate Question of Life, the Universe, and Everything"
Project website: Japanese graduate school entrance exam answers
The Kai Project is built around an open-source project and a public study archive. Community-maintained exam indexes, public solutions, study materials, and related documentation form the project's core public content and are intended to remain openly accessible to learners.
To support long-term maintenance, technical services, and community operations, the project may explore sustainable operation around learning tools, account features, data APIs, tutoring support, or partner integrations. These supporting services or partnership arrangements do not change the open-access nature of the core public content.
- Open past-exam and solution database across multiple universities and departments
- Exam-experience blog posts and preparation write-ups
- Per-question progress tracking with review reminders and a progress dashboard
- Built-in Markdown/LaTeX notes plus selectable inline annotations with highlighting, quick navigation, and editing
- Account-based progress, private problem sets, public nicknames, and a practice leaderboard powered by Supabase
- Local search and share-as-image for answer pages; web document reading requires a network connection
- Node.js
>=20 - Yarn
>=1.22.0
Install dependencies and start the development server:
yarn install
yarn devThen open http://localhost:3000.
To build and preview the production bundle locally:
yarn build
yarn serveUseful repository scripts:
yarn generate:universities
yarn generate:site-stats
yarn tags:generate
yarn content:validate
yarn tags:audit
yarn documents:validate
yarn content:export
yarn review:format
yarn api:validateyarn generate:universities: regeneratesrc/data/universities.jsafter changing thedocs/directory structure or_category_.jsonlabels.yarn generate:site-stats: regeneratesrc/data/siteStats.jsonandsrc/data/documentTitles.jsonfrom the public JSON API scan; development and production builds run it automatically.yarn tags:generate: regeneratedocs/tags.ymlfrom the subject files undersrc/data/tagTaxonomy/.yarn content:validate: validate contributor-editable JSON data undersrc/data/, including links, university metadata, and the tag taxonomy.yarn tags:audit: summarize site-wide school, subject, subsubject, topic, pending, and deprecated tag usage.yarn documents:validate: validate automatically derived document UUIDs and the current overrides / historical aliases stored only for renamed paths.yarn content:export: generate the complete client-sync snapshot atbuild/content-export/v1/kai-content-v1.json.gz; see CONTENT_EXPORT.md for its contract and import rules.yarn review:format: review answer-document formatting underdocs/before opening a PR.yarn api:validate: validate the structured data used by the public JSON API.
Contributor-editable content data lives under src/data/: links.json, universityMetadata.json, and the tagTaxonomy/ directory. Tag definitions are split by primary subject under tagTaxonomy/subjects/; global policy and school tags live alongside them. Ordinary new documents derive UUIDv5 directly from docId and require no identity-manifest update. Only a move or rename requires yarn documents:move -- <old-doc-id> <new-doc-id>. Development and production builds refresh siteStats.json and documentTitles.json automatically; the other generated files can be maintained with the scripts above.
Public content remains readable without Supabase credentials. Account-only features such as progress, notes, annotations, private problem sets, and the leaderboard require the following configuration; those records are written directly to the database and anonymous study data is not supported.
export SUPABASE_URL="https://your-project.supabase.co"
export SUPABASE_ANON_KEY="your-anon-key"
export HCAPTCHA_SITE_KEY="your-hcaptcha-site-key"
export PROBLEM_SETS_ENABLED="true"SUPABASE_URLandSUPABASE_ANON_KEYenable authentication and account data.HCAPTCHA_SITE_KEYis optional but recommended for abuse protection on the login/register page.PROBLEM_SETS_ENABLEDis a release flag. It must be exactlytrueto expose private problem-set UI; leave it unset until the latest schema is deployed and verified.
To enable account features end-to-end:
- Create a Supabase project. For a blank project, apply
src/services/schema.sqlonce as the baseline; do not reapply it to an existing database. - The current baseline has no pending historical migrations. Future schema changes will be added under
supabase/migrations/and applied by the deployment workflow. - Configure the auth security items noted in that SQL file, including rate limits, password policy, and hCaptcha.
- Add the site callback URLs in Supabase Authentication → URL Configuration, including
https://your-domain/auth/callbackandhttps://your-domain/reset-password. - Deploy
supabase/functions/content-submissionsso contribution authors are read from the unified public profile instead of request data. - Verify nickname and private problem-set RPCs with test accounts, then rebuild with
PROBLEM_SETS_ENABLED=true.
Registered users can request JSON API access in the developer center. After a project maintainer approves the request, the user can create an API key and read exam and answer data. API keys are shown only once when created; the database stores only SHA-256 hashes.
- Log in on the website and open
/developers, then enter the JSON API feature. You can also open/developers/apidirectly. - Submit an API access request. Intended use is optional; if the use involves an organization, bulk reuse, or commercial integration, please describe it in the request.
- Wait for project maintainer review. Once approved, the page will allow API key creation.
- Create an API key and save the
kai_live_...value immediately. - Call the content API with
Authorization: Bearer kai_live_.... The content API does not accept anonymous requests or login JWTs.
Available endpoints:
GET /v1/catalog: returns universities, departments, programs, years, and document counts.GET /v1/exams?university=tokyo-university&department=IST&program=cs&year=2024&include=content: queries exam documents;include=contentreturns markdown content.GET /v1/exams?subject=Computer-Science&subsubject=Computer-Science.Computer-Architecture&topic=Computer-Science.Computer-Architecture.Cache: filters by the derived learning taxonomy.GET /v1/exams/{doc_id}: returns one document by ID.
Exam rows include an immutable documentUuid, the original frontmatter tags, and derived taxonomy fields: schoolTags, learningTags, subjectIds, subsubjectIds, and topicIds. Ordinary new documents derive their UUID from the fixed namespace and docId without registration. When a path changes, run yarn documents:move -- <old-doc-id> <new-doc-id>; it stores only the rename exception and old-path alias in documentIdentityOverrides.json, so saved progress, notes, votes, and problem-set items keep the same identity. Topic IDs are namespaced as Subject.Subsubject.Topic; learningTags identifies whether each learning tag is a subsubject, concrete topic, or pending tag, and topic entries include short_id.
Examples:
curl -H "Authorization: Bearer kai_live_..." \
"https://your-project.supabase.co/functions/v1/kai-api/v1/catalog"
curl -H "Authorization: Bearer kai_live_..." \
"https://your-project.supabase.co/functions/v1/kai-api/v1/exams?university=tokyo-university&department=IST&program=cs&year=2024&include=content"
curl -H "Authorization: Bearer kai_live_..." \
"https://your-project.supabase.co/functions/v1/kai-api/v1/exams?subject=Computer-Science&subsubject=Computer-Science.Computer-Architecture&topic=Computer-Science.Computer-Architecture.Cache"Responses always include apiVersion, sourceUrl, license, and contentNotice. Core public content remains openly accessible; API access, bulk reuse, redistribution, commercial integration, and other uses beyond ordinary browsing and personal study are subject to the project's separately published content/API terms, and users are responsible for obtaining any necessary permissions from the relevant rights holders.
The project exposes exam data through Supabase Edge Functions while reusing the existing login system as the developer identity layer.
- Apply src/services/schema.sql once only for a blank database; it already contains the complete current structure. Never reapply the baseline to an existing database. Existing production projects whose three 20260718 migrations were squashed should run the baseline finalization SQL once before adding another migration.
- Deploy the functions in supabase/functions. Production GitHub Actions uses
SUPABASE_ACCESS_TOKENandSUPABASE_PROJECT_REFfor function deployment;SUPABASE_DB_PASSWORDis additionally required only when a new database migration exists:
npx supabase functions deploy developer-api-keys --project-ref "$SUPABASE_PROJECT_REF"
npx supabase functions deploy kai-api --project-ref "$SUPABASE_PROJECT_REF"
npx supabase functions deploy agent-context --project-ref "$SUPABASE_PROJECT_REF"kai-api and agent-context share the static body loader in supabase/functions/_shared/published-content.ts. The Supabase CLI and GitHub Actions bundle it automatically, so there is no file to copy and no function to update manually in the Dashboard.
- Markdown bodies are not mirrored into PostgreSQL; the build publishes them by stable UUID under
/api-content/v1/documents/. GitHub Actions does not storeSUPABASE_SERVICE_ROLE_KEY. After deployment, a maintainer temporarily supplies the key in a trusted local terminal and syncs identities plus the lightweight catalog only:
printf 'Supabase service-role key: '
IFS= read -r -s SUPABASE_SERVICE_ROLE_KEY
printf '\n'
export SUPABASE_SERVICE_ROLE_KEY
SUPABASE_URL="https://your-project.supabase.co" yarn catalog:sync
unset SUPABASE_SERVICE_ROLE_KEYThis command upserts UUIDs, paths, titles, links, tags, taxonomy, and content hashes, then prunes stale document_catalog rows. It never uploads Markdown or modifies notes, progress, problem sets, or any other user table. The JSON API and Agent fetch bodies from the static JSON published with the website.
This input method keeps the key out of shell history; never commit it or store it in GitHub Actions for this workflow.
After document paths have moved, you may run the doc_id canonicalization SQL manually in Supabase SQL Editor during a low-traffic maintenance window. Take a backup and complete yarn catalog:sync first. The repeatable script rewrites legacy doc_id snapshots in application tables to document_registry.current_doc_id while preserving UUIDs, user rows, timestamps, note versions, and all historical aliases. Every final stale_row_count should be 0.
- Confirm the Supabase Function secrets include
API_LOG_SALT, and disable JWT verification for both functions. - Review requests in the
api_access_requeststable from the Supabase Dashboard: setstatustoapprovedto allow key creation, or userejected/revokedto deny or pause access.
The first version gives all approved users the same baseline access configuration: 60 requests/minute and up to 3 active keys. api_access_requests and api_keys already reserve fields such as plan and commercial_allowed for future partner access, institutional scenarios, and tiered quota management.
The project encourages community contributions through multiple channels:
- Git Pull Requests: For contributors familiar with Git
- Email Submissions: For users who prefer to send content via email
- Community Discussion: Discuss exam solutions and preparation experience via Discord or the QQ group, or report incorrect answers through GitHub issues!
Contributions merged into the public exam archive and public solution library become part of The Kai Project's core public content and are intended to remain openly accessible. Sustainable operation around learning tools, data APIs, tutoring support, or partner integrations does not change the open-access nature of these accepted core public contributions.
All answer-document contributions under docs/ should follow a consistent structure.
Each answer markdown document should look like this:
---
sidebar_label: 'Title displayed in sidebar'
tags:
- Tokyo-University
- Subsubject-Tag
- Topic-Tag
---
# Full page title
## **Author**
[Your Name](https://example.com)
## **Description**
Problem statement, transcription, or a short summary of the question.
## **Kai**
Your solution, derivation, explanation, or notes.Rules enforced by the repository formatter:
sidebar_labelis required.tagsis required.- The first non-empty line after frontmatter must be an H1 title.
## **Author**is required.## **Description**and## **Kai**are optional individually, but at least one of them must exist.- If both are present, keep the order
Author→Description→Kai.
Tag rules:
- Prefer existing canonical subsubject IDs and namespaced topic IDs from the subject taxonomy files. Top-level subject tags and legacy short topic tags are not valid frontmatter tags.
- Subject associations in the taxonomy should be strong associations found in actual problem content, not broad theoretical overlap.
- School tags remain compatible, but the site primarily derives school metadata from the
docs/school/department/...path. - Correct new subsubject or topic tags are allowed;
yarn review:formatreports them as warnings instead of blocking the PR. - Tags missing from the current taxonomy are reported as new tags so contributors can check their spelling or request review.
- If a document only has a school tag and no learning tag, the formatter reports a warning. If it has only a subsubject tag, the formatter suggests adding a more concrete topic when the problem statement has enough signal.
Before opening a PR, please run:
yarn review:format
yarn tags:auditBlog posts should use author IDs defined in blog/authors.yml, not inline author objects.
- Add a new author entry to blog/authors.yml, or reuse an existing one.
- Reference that author ID in the post frontmatter.
Example:
---
title: Post title
authors: yourAuthorId
tags: [Tag1, Tag2]
---For reference, existing posts such as blog/2025-04-02-furry.md and blog/2025-07-10-unagoya.md already use author IDs.
Files must be placed in the correct directory structure and follow naming conventions:
- Base directory:
docs/ - Hierarchy:
school/department/major/academic year/ - Filename: should indicate the specific content details (school abbreviation, exam year/month, subject, question number)
docs/
├── <university>/
│ ├── <department>/
│ │ ├── <specialization>/
│ │ │ ├── <academic year>/
│ │ │ │ └── <exam_file>.md
blog/
├── <blog_post>.md
Example:
docs/tokyo-university/engineering/ap/2020/ap_201908_phys_3.md
This corresponds to:
- School: Tokyo University
- Department: Engineering
- Major: Applied Physics (
ap) - Year and month of the exam:
201908 - Subject: Physics
- Question number:
3
For contributors who are not familiar with Git, the project accepts contributions via email.
Steps for email submission:
- Prepare your content following the formatting guidelines above.
- Please read our Contributor License Agreement (CLA).
- Send your content to the project email address: 376672994@qq.com.
- Include the following statement in your email body: "I have read and agree to The Kai Project CLA."
- Project maintainers will review your submission and add it to the repository if approved.
Before we can merge your contributions, you must sign our Contributor License Agreement (CLA).
For GitHub users (Pull Requests): CLA signing is now checked automatically by the workflow in this repository: .github/workflows/cla-check.yml.
After opening a PR, please add a signature statement in any one of these places (by the PR author):
- PR description
- PR conversation comment
- PR review comment
Accepted statements:
I have read the CLA Document and I sign the CLA
I have read and agree to The Kai Project CLA.
If no statement is detected, the CLA 检查 workflow will fail and leave a reminder comment. Once a valid statement is added, the check will pass automatically.
For non-GitHub users (Email submissions): As mentioned above, include the statement "I have read and agree to The Kai Project CLA." in the email body.
Join The Kai Project community on Discord or QQ to discuss exam solutions, share preparation experience, and provide feedback. You can also report incorrect answers through GitHub issues.
Feedback received through community discussions may be incorporated into the repository by maintainers or used to guide future contributions.
All code contributions to The Kai Project are subject to the GNU Affero General Public License v3.0.
Public solution, study-material, and documentation contributions are governed by the Contributor License Agreement (CLA). The project keeps core public content accessible as part of its open-source public archive while preserving room for long-term operation around supporting tools, APIs, service support, and partnership scenarios.
In addition, exam question copyright belongs to the respective schools/institutions.
The Kai Project acknowledges all contributors in maintaining and expanding this knowledge base.
If you believe any content infringes on your rights, please contact the project maintainers immediately.
