Skip to content

feat: add tenant-safe learning kernel - #4

Open
seonghobae wants to merge 8 commits into
agent/bootstrap-learning-managementfrom
feat/external-learner-kernel
Open

feat: add tenant-safe learning kernel#4
seonghobae wants to merge 8 commits into
agent/bootstrap-learning-managementfrom
feat/external-learner-kernel

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

What changed

  • adds a Rust domain kernel for non-employee and employee-linked learner affiliations
  • keeps identity, source evidence, and policy revisions as explicit references
  • evaluates completion only from matching tenant/learner evidence and produces a replay fingerprint
  • adds a PostgreSQL migration with 3NF relations, effective-dated affiliations, cross-tenant composite foreign keys, RLS policies, and immutable evidence references
  • runs the API with a CI NOSUPERUSER NOBYPASSRLS application role rather than the PostgreSQL provisioning superuser
  • adds disposable migration rollback/reapply rehearsal and customer, self-sponsored, multi-tenant, and duplicate-request coverage
  • reconciles the current PR1 base and keeps the gap baseline at the current PR4 head

Why

This is the smallest executable slice for issue #3. It makes the documented learner/identity/authority boundary enforceable before adding HTTP and external-provider adapters.

Validation

  • exact current head 4ce89f96310d41eaa3b12cf4afa12c36a72462dd
  • cargo +1.97.1 fmt --all -- --check
  • cargo +1.97.1 test --workspace --all-targets --locked (5 tests)
  • cargo +1.97.1 clippy --workspace --all-targets --locked -- -D warnings
  • RUSTDOCFLAGS='-D warnings' cargo +1.97.1 doc --workspace --no-deps --locked
  • local PostgreSQL 18.4 with a NOSUPERUSER NOBYPASSRLS role: migration, health, learner 201, duplicate 409, rollback, and reapply
  • actionlint and diff checks

Closes #3 partially; the end-to-end enrollment journey and provider adapters remain follow-up work. Repeated-enrollment journey coverage remains in scope for the stacked enrollment slice because this PR has no enrollment relation or endpoint.

@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8409f18f-132e-4c58-b0e0-8dfe69927b0b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@seonghobae
seonghobae force-pushed the feat/external-learner-kernel branch from 4eddbeb to e7aa3eb Compare August 20, 2026 10:39
@seonghobae

Copy link
Copy Markdown
Contributor Author

@opencode-agent Please review the current exact head 6b4b89f against base agent/bootstrap-learning-management; earlier 4eddbeb evidence is stale. Check the Rust domain boundaries, PostgreSQL migration/RLS/effective dating/composite tenant keys, SQLx API transaction and duplicate/multi-tenant behavior, CI smoke test, and public documentation. Return a current-head semantic verdict only; do not self-approve.

@seonghobae

Copy link
Copy Markdown
Contributor Author

@opencode-agent Re-review current exact head 7c7a846 against agent/bootstrap-learning-management; 6b4b89f is stale after documentation/CI changes. Verify the public-doc gate, real PostgreSQL API smoke path, identity/tenant model, and implementation-vs-planned claims. Return a current-head semantic verdict only; do not self-approve.

@seonghobae
seonghobae force-pushed the feat/external-learner-kernel branch from 7c7a846 to b832ed6 Compare August 20, 2026 11:07
@seonghobae

Copy link
Copy Markdown
Contributor Author

@opencode-agent Please review the current exact head b832ed6b7b79ef036a09af4876e90944536c958c against base agent/bootstrap-learning-management; the previous 7c7a846 review request is stale after rebase. Re-check Rust/PostgreSQL/API behavior, migration/RLS/tenant boundaries, and current CI/docs. Return an independent semantic verdict only; do not self-approve.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current-head review request for 8490f15. Re-verify the exact-head diff, required Checks, tenant/security boundaries, and any valid review findings after the shared Rust component-install fix. COMMENT only; no approval or protected-merge bypass is requested.

Copy link
Copy Markdown
Contributor Author

@opencode-agent Review only exact current head b5b4f6f against base 7ba5864; do not update, approve, merge, or bypass protections. Focus on the migration-once smoke fix and learner registration regression risk.

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor Author

@opencode-agent Review only exact current head 9de3bcf against base 7ba5864; do not update, approve, merge, or bypass protections. Re-check the non-superuser/NOBYPASSRLS CI role, disposable rollback/reapply evidence, and added customer/self-sponsored/multi-tenant tests. Reassess the repeated-enrollment point against PR4's learner-only scope.

Copy link
Copy Markdown
Contributor Author

@opencode-agent Review only exact current head fe1b890 against base 7ba5864; do not update, approve, merge, or bypass protections. Re-check the final docs-only head after the non-superuser/NOBYPASSRLS and rollback-evidence fixes; prior 9de3bcf review request is stale.

…ent' into feat/external-learner-kernel

# Conflicts:
#	docs/product-technical-gap-baseline.md

Copy link
Copy Markdown
Contributor Author

@OpenCode review only the exact current head 4ce89f9 against base 61f9d8c. Re-check the least-privilege PostgreSQL role, rollback/reapply rehearsal, tenant-affiliation coverage, merge-base reconciliation, and current workflow. Leave an independent review or approval for this exact head only. Do not update code, merge, self-approve, bypass protections, or treat queued Checks as proof.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment on lines +116 to +119
sqlx::query("SELECT set_config('app.tenant_id', $1, true)")
.bind(tenant_id.to_string())
.execute(&mut *transaction)
.await?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟨 Learner registration endpoint has no authentication or tenant authorization

The POST /v1/tenants/{tenant_id}/learners handler (crates/lms_kernel/src/bin/lms_api.rs:95-163) accepts the target tenant_id directly from the URL path and then sets the row-level-security context from that same untrusted value (set_config('app.tenant_id', $1, true) at crates/lms_kernel/src/bin/lms_api.rs:116-119). There is no authentication middleware and no check that the caller is entitled to act on the supplied tenant. Because the caller both chooses the tenant and supplies the RLS context, the PostgreSQL RLS boundary provides no protection against a malicious caller: anyone who can reach the service can create/enumerate learner memberships in any tenant (and, via the global login_identity_reference/learner_profile lookups, learn the stable learner_id for a guessed external identity).

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant