Skip to content

Feat/python sdk cipher - #1453

Draft
yujiyokoo wants to merge 3 commits into
feat/python-sdk-declaration-meaningfrom
feat/python-sdk-cipher
Draft

yujiyokoo wants to merge 3 commits into
feat/python-sdk-declaration-meaningfrom
feat/python-sdk-cipher

Conversation

@yujiyokoo

Copy link
Copy Markdown
Contributor

Summary

A declared class no longer prints plaintext: repr, str and rich's __rich_repr__ show <sealed> for every sealed field. Cipher(User) now exists. It checks that the class is declared and unchanged, then finds the credentials the way every SDK does, with no request. encrypt, decrypt and query come in later tickets.

Changes

Redaction (_redact.py). @stackencrypt.model replaces __repr__, __str__ and __rich_repr__. A passthrough field shows its value, a sealed field shows <sealed>, and an Ignore field is left out. A value is read from the instance's __dict__ or a storage slot only, so a SQLAlchemy column that is not loaded runs no SELECT. A generated method is replaced. A hand-written one is refused with declaration_repr_present, unless the decorator is given redact=False.

Cipher (_cipher.py). Cipher(cls) refuses an undeclared class, a subclass of a declared class, or a non-class with declaration_not_decorated. A class changed after decoration is declaration_class_changed, with reason methods, fields or constructor. Then it calls the engine for the credentials. A refusal is an AuthenticationError with the engine's code, such as stack_auth_not_authenticated.

Credentials (crates/core/src/credentials.rs). stack-auth's AutoStrategy reads CS_CLIENT_ACCESS_KEY and CS_WORKSPACE_CRN, then the profile stash auth login writes. It is given a transport that sends nothing, so no HTTP client is built and the http feature stays off. Each refusal names the variable or file to fix.

Placeholder. Python prints <sealed>, not Go's [sealed]. [sealed] is a valid tag in rich's markup, so rich.print(repr(user)) would show the field as empty. The ADR and the spec record it.

Verification

  • pytest: 228 passed and 2 skipped, on Python 3.14 and 3.9. The redaction tests run on a dataclass, attrs, Pydantic, NamedTuple and SQLAlchemy.
  • cargo test --locked, clippy, cargo fmt --check, mypy strict, and pyright are clean.
  • pnpm run test:scripts: 83 of 83 files pass.
  • The six platform wheels are built by CI only. This is the first branch with stack-auth in them.

Related

Closes #1388. Part of #1341.

Review notes

  • Start with _redact.hand_written and _value, then check_unchanged in _declaration.py.
  • Serialisation is not printing: dataclasses.asdict, model_dump and vars still return plaintext, by design.
  • A SQLAlchemy Ignore field under Annotated loads as one object, not a list. That is a spec problem, noted on Python SDK: core call over psycopg, on EQL v4 and Stack Encrypt #1341.

🤖 Generated with Claude Code

yujiyokoo and others added 3 commits October 30, 2026 18:03
repr and str show [sealed] for sealed fields. Cipher(cls) checks the
class again and finds the credentials, with no request and no HTTP
client.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Credentials are found without building an HTTP client, and each
refusal names the variable or file to fix. Redaction reads the class's
own dict, covers rich, and runs no descriptor that may do work.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
repr and str show [sealed] for sealed fields. Cipher(cls) checks the
class again and finds the credentials, with no request.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Oct 30, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 5b792d5

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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

This branch has not been deployed

No deployments
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