Oracle3
Oracle3: 예측시장 거래 엔진·MCP 서버
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Automatic Expenditure Tracker & Splitter
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Split expenses with friends, on a platform where every DevOps tool does real, checkable work.
The live app · Every decision · The demo-day runbook · The evidence
An expense-splitting app, installable on a phone. You record who paid for what in groups and trips, and it settles everyone up in the fewest payments, with UPI links. It is built with Next.js 16, Prisma on Neon Postgres, and NextAuth v5, and it is live on Vercel for real users.
A DevOps platform around it, built so that nothing in the pipeline only says it worked.
main is tested, built once, scanned, signed and attested./ops shows every tool's verdict, read live from the tool itself.Where it runs:
Every decision, what was rejected, and the measurement behind each claim is in docs/DECISIONS.md: 117 decisions, with the open problems at the end.
flowchart LR
phones(["Phones and browsers"])
gh["GitHub<br/>Actions · GHCR · deployments"]
vercel["Vercel<br/>the live app, always on"]
neonProd[("Neon<br/>production")]
subgraph edge["AWS edge, permanent"]
cf["API Gateway<br/>the HTTPS edge"]
end
subgraph eks["EKS, on demo days"]
alb["ALB<br/>the app serves only the edge"]
app["SplitX pods<br/>autoscaled 2 to 10"]
redis[("Redis")]
opsapi["ops-api"]
lab["traffic lab<br/>k6"]
jenkins["Jenkins"]
nexus[("Nexus<br/>evidence")]
mon["Prometheus · Alertmanager<br/>Grafana · Loki"]
kyverno["Kyverno"]
end
neonDemo[("Neon<br/>demo branch")]
phones --> vercel --> neonProd
phones --> cf --> alb --> app
app --> redis
app --> neonDemo
app --> opsapi --> mon
opsapi --> lab -->|"load, through the edge"| cf
gh -->|"signed webhook"| cf
alb -->|"/generic-webhook-trigger/"| jenkins
jenkins -->|"deploys by digest"| app
jenkins --> nexus
kyverno -.->|"admits only signed images"| app
classDef aws fill:#fff4e5,stroke:#e8891b,color:#4a2b00
classDef k8s fill:#eaf1ff,stroke:#3b6fd8,color:#0f2a5c
classDef ext fill:#f1f0f7,stroke:#6f6a8c,color:#231f36
classDef data fill:#e8f7ef,stroke:#1f9d63,color:#0b3a24
class cf,alb aws
class app,opsapi,lab,jenkins,mon,kyverno k8s
class gh,vercel,phones ext
class neonProd,neonDemo,redis,nexus data
The same manifests run on both clusters: one Kustomize base, with an overlay each for Kind and AWS (D-093). Kind proves the platform every night on a GitHub runner; EKS serves the demo.
flowchart TB
subgraph actions["GitHub Actions, on every merge to main"]
direction LR
push["Merge to main"] --> ci["CI: types, unit, integration,<br/>real Postgres, manifests,<br/>IaC, Sonar"]
ci --> build["Build once<br/>Docker Buildx Bake"]
build --> scan["Trivy gate<br/>no fixable critical or high"]
scan --> sign["cosign: sign, keyless<br/>attest SBOM + vulnerabilities"]
end
sign --> ghcr[("GHCR<br/>by digest")]
sign --> dep["GitHub deployment<br/>kind, and eks on AWS days"]
subgraph cluster["The cluster"]
direction LR
way{"Kind: smee.io + relay<br/>EKS: API Gateway + ALB"} --> jen["Jenkins<br/>checks the HMAC,<br/>then the image's signature"]
jen --> apply["Applies that commit's<br/>overlay, image by digest"]
apply --> ok{"Healthy<br/>through the edge?"}
ok -->|yes| done["Evidence to Nexus<br/>success to GitHub"]
ok -->|no| back["Rolled back<br/>failure to GitHub"]
end
dep -->|"webhook, HMAC-signed"| way
classDef step fill:#eaf1ff,stroke:#3b6fd8,color:#0f2a5c
classDef trust fill:#efeafe,stroke:#6d4ee8,color:#27165e
classDef good fill:#e8f7ef,stroke:#1f9d63,color:#0b3a24
classDef bad fill:#fdecee,stroke:#c2183a,color:#5c0a1b
class push,ci,build,apply,way step
class scan,sign,ghcr,dep,jen trust
class done,ok good
class back bad
The details are in D-054 and D-055, the runs in D-099.
flowchart LR
oidc["GitHub OIDC token<br/>ci.yml on refs/heads/main"] --> fulcio["Fulcio<br/>a certificate for minutes"]
fulcio --> sig["cosign signature<br/>+ SBOM + vulnerability report"]
sig --> rekor[("Rekor<br/>public log")]
sig --> ghcr[("GHCR")]
ghcr --> jenkins["Jenkins verifies<br/>before it deploys"]
ghcr --> kyverno["Kyverno verifies<br/>at admission, whoever asks"]
jenkins --> nexus[("Nexus<br/>stores evidence it verified")]
classDef trust fill:#efeafe,stroke:#6d4ee8,color:#27165e
classDef store fill:#e8f7ef,stroke:#1f9d63,color:#0b3a24
class oidc,fulcio,sig,jenkins,kyverno trust
class rekor,ghcr,nexus store
There is no signing key to leak. The certificate names the workflow, the branch and the commit. The cluster checks whose signature it is, not only that one exists: the same image, judged against another workflow, is refused (D-060). Anyone can check a release:
cosign verify ghcr.io/sayandip-jana-1018/splitx:<first 12 characters of a commit on main> \
--certificate-identity https://github.com/Sayandip-Jana-1018/SplitX/.github/workflows/ci.yml@refs/heads/main \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
flowchart TB
user(["Visitors"]) --> cf["API Gateway<br/>HTTPS · a route while the platform is up"]
subgraph vpc["VPC 10.0.0.0/16 · ap-south-1 · two zones"]
subgraph pub["Public subnets"]
alb["ALB<br/>refuses the internal paths"]
nat["NAT gateway"]
end
subgraph priv["Private subnets"]
nodes["3 m7i-flex.large nodes<br/>a 4th from the Cluster Autoscaler"]
end
end
cf -->|"X-Origin-Verify"| alb --> nodes
nodes --> nat --> neon[("Neon demo branch")]
sm["Secrets Manager"] -.->|"External Secrets"| nodes
pod["EKS Pod Identity"] -.->|"a role per workload"| nodes
ebs[("EBS gp3, encrypted")] --- nodes
classDef aws fill:#fff4e5,stroke:#e8891b,color:#4a2b00
classDef data fill:#e8f7ef,stroke:#1f9d63,color:#0b3a24
class cf,alb,nat,sm,pod aws
class neon,ebs data
Who makes what:
flowchart TB
subgraph cfn["CloudFormation: the account layer, permanent"]
boot["splitx-bootstrap<br/>the state bucket, GitHub OIDC,<br/>the deploy and teardown roles,<br/>the permissions boundary"]
guard["splitx-guardrails<br/>the $15 budget,<br/>SNS alerts"]
end
subgraph tf["Terraform"]
edge["terraform/edge<br/>permanent, $0 idle<br/>an API Gateway HTTP API"]
plat["terraform/platform<br/>one day at a time<br/>the VPC, EKS and its add-ons,<br/>the nodes, Pod Identity roles, alarms"]
end
subgraph up["cluster-up --target eks"]
sw["External Secrets, monitoring,<br/>the ALB controller, Cluster Autoscaler,<br/>Kyverno, Jenkins, Nexus,<br/>the app, ops-api, the traffic lab"]
end
boot --> tf
plat --> up
classDef cfn fill:#fff4e5,stroke:#e8891b,color:#4a2b00
classDef tf fill:#efeafe,stroke:#6d4ee8,color:#27165e
classDef k8s fill:#eaf1ff,stroke:#3b6fd8,color:#0f2a5c
class boot,guard cfn
class edge,plat tf
class sw k8s
aws-up builds and verifies it, aws-down proves nothing is
left, and a scheduled aws-down at 23:30 IST catches a forgotten one
(D-087 to D-095).The setup is in AWS_SETUP_GUIDE.md, and each AWS day in docs/DEMO_DAY.md.
flowchart LR
app["App pods<br/>/api/metrics, with a token"] -->|"ServiceMonitor"| prom["Prometheus"]
jen["Jenkins"] --> prom
kyv["Kyverno"] --> prom
ks["node-exporter ·<br/>kube-state-metrics"] --> prom
prom -->|"SplitX rules, tested with promtool"| am["Alertmanager"] -->|"email"| inbox(["Inbox"])
alloy["Alloy<br/>pod logs, by the API"] --> loki["Loki"]
prom --> graf["Grafana<br/>dashboards as code"]
loki --> graf
prom --> opsapi["ops-api"]
loki --> opsapi
am --> opsapi
opsapi --> page["/ops"]
classDef src fill:#eaf1ff,stroke:#3b6fd8,color:#0f2a5c
classDef store fill:#e8f7ef,stroke:#1f9d63,color:#0b3a24
classDef out fill:#efeafe,stroke:#6d4ee8,color:#27165e
class app,jen,kyv,ks,alloy src
class prom,loki,am store
class graf,opsapi,page,inbox out
splitsj.vercel.app every 15 minutes, as a browser would, and the
database once an hour. A check that fails twice fails the run, which GitHub emails, and opens an
incident issue that closes itself on recovery (D-106). /ops shows its
newest verdict.| Tool | What it does here | Where | What proves it |
|---|---|---|---|
| Git, husky, commitlint, gitleaks | Conventional commits. No secret reaches a commit, or the history. | .husky/, commitlint.config.js | Every commit in this repository |
| GitHub Actions | CI, the release, the Kind end-to-end run, and AWS up, verify and down. Every 15 minutes, checks of the live site, which open an incident issue when one fails and close it on recovery. | .github/workflows/, scripts/uptime.mjs | The badges above |
| GitHub Deployments and webhooks | Each release is announced as a deployment. The deployer reports back on it. | ci.yml, jenkins/deploy.mjs | cd:verify: GitHub's own delivery started the build |
| CodeQL, Dependabot | Code scanning, and vulnerable dependencies | codeql.yml, .github/dependabot.yml | The Security tab; counted on /ops |
| SonarQube Cloud | The quality gate on new code: coverage, ratings, duplication | sonar-project.properties, CI's sonar job | The gate badge above |
| Vitest | Unit, property, integration (Redis) and database (Postgres) tests | tests/ | CI's verify, integration and database jobs |
| Playwright | The flows a classroom uses, on a phone's browser against a production build: sign-up, a group and its invite, expenses, settling up, removing a member, a shared phone after sign-out, and a receipt read on the phone. Any Content Security Policy violation fails a test, which is what lets the policy be enforced. | tests/e2e/, playwright.config.ts | CI's e2e job, which a release waits for |
| Docker, Buildx Bake, Compose | One image per release, built once, with no npm in it at run time. Compose runs local development. | Dockerfile, docker-bake.hcl, docker-compose.yml |
| What | Where |
|---|---|
The whole platform, end to end, nightly. Kind on a GitHub runner, a delivery through GitHub's own webhook, then every verifier: k8s:verify, cd:verify --rollback and ops-verify. | The newest Kind end-to-end run: its summary has every result table, and its artifact keeps the reports. Runs 1 to 8 are in D-099. The four reports below are run 8's, from 25 September 2026. |
| The cluster: probes against a real database outage, a release under traffic, network policy, monitoring end to end | docs/evidence/kubernetes.md |
| Delivery: the webhook gate, the deploy account, admission, and a rollback | docs/evidence/delivery.md |
The control room: /ops as an operator reads it, from ops-api and the traffic lab | docs/evidence/ops.md |
| The run itself: the runner's memory throughout, each pod's use, and Jenkins' two consoles (the release, then the rollback) | docs/evidence/kind-e2e/ |
| Load: a classroom under production limits, overload on two pods, the autoscaler | docs/evidence/load-tests.md |
| The image against a naive build | docs/evidence/image-comparison.md |
/scale: the public demo page. Plan a trip for up to 2,000 people, and see which pod answered.flowchart TB
bal["Each member's net balance"] --> opp["1. Exact opposites pay each other"]
opp --> small{"16 people or fewer left?"}
small -->|yes| dp["2. Dynamic programming over subsets:<br/>provably the fewest payments"]
small -->|no| trip["3. Triples that cancel,<br/>then largest first"]
dp --> keep["4. Keep the classic plan,<br/>unless it needs more payments"]
trip --> keep
keep --> plan["The settle-up plan"]
classDef step fill:#eaf1ff,stroke:#3b6fd8,color:#0f2a5c
classDef out fill:#e8f7ef,stroke:#1f9d63,color:#0b3a24
class bal,opp,small,dp,trip,keep step
class plan out
A group of k people whose balances add up to zero settles in k − 1 payments, so the fewest payments means the most such sets. A simulated 2,000-person event needs 1,818 payments instead of 1,999; repaying each expense directly would take 23,602 (D-023).
The app, locally (Node 24):
npm ci
cp .env.example .env
Then fill in .env, and start Postgres and Redis with docker compose up -d postgres redis.
npm run dev
Tests:
npm test: unit and property tests, 1,465 of them.npm run test:integration: against a real Redis.npm run test:db: against a real Postgres.npm run test:alerts: the alert rules, with promtool.Kubernetes: the platform on Kind.
npm run k8s:up builds the cluster. Then npm run k8s:verify, and
npm run cd:verify -- --rollback.On AWS: set it up once with AWS_SETUP_GUIDE.md, then follow docs/DEMO_DAY.md on each AWS day.
| Path | What |
|---|---|
src/ | The app: pages, API routes, and src/lib (the settlement planner, rate limiting, /ops) |
ops/ | ops-api and the traffic lab, the cluster half of /ops |
k8s/, helm/platform/, policy/, monitoring/ | The platform: manifests, pinned charts, admission policy, dashboards and alerts |
jenkins/, nexus/ | Delivery: Jenkins' job, its permissions and relay; Nexus and its setup |
terraform/, cloudformation/ | AWS: the edge, the platform, the account layer |
scripts/ | cluster-up, the verifiers, the load and AWS helpers |
docs/ | DECISIONS, the runbook, the evidence |
This project is private and not open-source.
| 81 MB against a naive 1,091 MB; 0 vulnerabilities against 4,139 (evidence) |
| Trivy | Gates each release on fixable critical and high vulnerabilities, and scans the IaC | CI's release and iac jobs | A release with such a finding is never signed |
| cosign and Sigstore | Keyless signatures and attestations | CI's release job | cosign verify, as above |
| GHCR | The registry, holding images by digest | ghcr.io/sayandip-jana-1018/splitx | The digest on /ops |
| Jenkins | The deployer: a webhook gate, signature checks, rollback, all as code | helm/platform/jenkins.values.yaml, jenkins/ | cd:verify: 16 checks, including a release rolled back |
| Nexus | Each deployment's evidence, verified before it is stored | nexus/ | ops-verify: the evidence is complete |
| smee.io relay | Carries GitHub's webhook to a cluster GitHub can't reach (Kind) | jenkins/relay/ | cd:verify: a signed delivery arrives intact |
| Kubernetes: Kind and EKS | The same platform on both | k8s/, k8s/kind/, terraform/platform | k8s:verify: 37 live checks on Kind, and a target for EKS |
| Kustomize, Helm | One base with an overlay per cluster; third-party charts pinned | k8s/overlays/, helm/platform/charts.json | CI renders every overlay against the Kubernetes 1.35 schemas |
| HPA, metrics-server, Cluster Autoscaler | Pods from 2 to 10 on CPU, and a fourth node on EKS | k8s/base/hpa.yaml | The traffic lab: 2 to 10 pods in every Kind run |
| ingress-nginx, AWS Load Balancer Controller | The way in on Kind, and on EKS | helm/platform/ | No request lost while every pod was replaced (2,391 of 2,391 in run 8) |
| Kyverno, Pod Security, NetworkPolicies | Refuse unsigned images, privileged pods, and cross-namespace traffic | policy/, k8s/base/networkpolicy.yaml | k8s:verify and cd:verify: each refusal tested live |
| External Secrets Operator | Kubernetes Secrets from AWS Secrets Manager, on EKS | k8s/eks/secrets/ | k8s:verify --target eks |
| Prometheus, Alertmanager, Grafana | Metrics, alerts by email, dashboards as code | monitoring/, k8s/base/prometheusrule.yaml | npm run test:alerts; k8s:verify: every dashboard query runs |
| Loki, Grafana Alloy | Logs, searchable by request ID | helm/platform/loki.values.yaml, alloy.values.yaml | k8s:verify follows one request into the logs |
| k6 | The traffic lab, and the load tests | ops/lab/, scripts/load-run.mjs | Load tests; ops-verify |
| CloudFormation | The account layer: the state bucket, OIDC, the bounded roles, the budget | cloudformation/ | cfn-lint and Trivy in CI; both stacks deployed |
| Terraform | The edge and the platform | terraform/edge, terraform/platform | validate, tflint and Trivy in CI |
| AWS services | VPC, EKS, EBS, ALB, API Gateway (the HTTPS edge), S3, CloudWatch, Secrets Manager, IAM with OIDC and a boundary, Pod Identity, Budgets, SNS | terraform/, cloudformation/ | aws-up checks each as it builds; aws-verify |
| The ledger: production's balances audited read-only | docs/evidence/ledger-audit.json, D-069 |
| EKS | The eks-verify artifact of each AWS day's aws-up run. The first is the rehearsal. |