Skip to content

feat(accounts): add platform credential slots - #436

Open
veryCrunchy wants to merge 119 commits into
feature/account-registry-foundationfrom
feature/account-credential-slots
Open

feat(accounts): add platform credential slots#436
veryCrunchy wants to merge 119 commits into
feature/account-registry-foundationfrom
feature/account-credential-slots

Conversation

@veryCrunchy

@veryCrunchy veryCrunchy commented Sep 1, 2026

Copy link
Copy Markdown
Member

Outcome

  • Add bounded Android and desktop credential slots keyed by the opaque local account identity from feat(accounts): add account identity registry foundation #429.
  • Migrate existing session storage without associating one account's metadata with another account's credential.
  • Serialize credential transitions with account-owned operations, retire private caches and pending work during removal, and preserve recovery state after interrupted cleanup.
  • Expose the shared account credential service contract. This is a foundation for account management, not a claim of completed multi-account product support.

Advances #172. This PR depends on #429.

Latest review revision

Commit eadef3dc989977bcb507865ed2bfb6743f9e9136 fixes DocumentsProvider metadata lookups reacquiring the non-reentrant account lease already held by rename, delete, move, and nested-create operations. Ordinary file reads still acquire and validate their own lease. Focused regressions cover held-lease lookups, stale ordinary reads, cache/error/cancellation behavior, and parent-directory validation.

One correctness finding remains open: malformed account-removal cleanup journals can block all account activation without a usable in-app recovery flow. An entry may contain no recoverable ownership identity. Ignoring it could revive pending-delete private state, while a blanket purge could erase offline files or drafts. No automatic journal discard or private-data purge has been added; recovery policy and explicit user consent need a maintainer decision before merge.

Validation

  • Hosted build/test, Windows packaging, capture, security, and preview checks passed at previous head 31c7c7e2458c3aaf0f78568e122cb951742d7642.
  • Full repository, Kotlin architecture, and diff checks passed for the new provider-deadlock fix.
  • Android execution of the newly added tests and compilation of that revision are pending. The dedicated build host became unavailable; the pushed head must pass hosted checks before merge.
  • Earlier focused Android credential persistence, desktop credential persistence, APK assembly, and desktop packaging runs passed during this PR's development. They do not replace validation of the latest revision.

Compatibility and evidence limits

Android and desktop JVM targets are affected. No device, emulator, live Nextcloud server, macOS keychain, Windows Credential Manager, or Linux Secret Service end-to-end verification is claimed for this revision. Corrupt, mismatched, and unsupported credential state remains fail-closed rather than being overwritten.

No visual changes in the provider-deadlock revision.

@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #436 / NC Native September 1, 2026 20:42 Destroyed
@obiente-cloud

obiente-cloud Bot commented Sep 1, 2026

Copy link
Copy Markdown

Obiente preview

NC Native · d3a3482e76ac · Ready

Open preview

View in Obiente

Obiente updates this comment as the preview changes.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review 🔄 Running since 2026-09-04T21:39:13.268005Z a4bdee1 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #436 / NC Native September 1, 2026 20:43 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #436 / NC Native September 1, 2026 20:49 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b5e6014e2e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #436 / NC Native September 1, 2026 21:49 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d0bb0e52ff

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #436 / NC Native September 3, 2026 20:37 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6dabe16e75

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread androidApp/src/main/kotlin/dev/obiente/nextcloudnative/NextcloudFileSyncWorker.kt Outdated
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #436 / NC Native September 3, 2026 20:51 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5849f1719f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #436 / NC Native September 3, 2026 21:06 Destroyed
@veryCrunchy
veryCrunchy force-pushed the feature/account-credential-slots branch from d6d0017 to 4daca37 Compare September 3, 2026 21:08
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #436 / NC Native September 3, 2026 21:08 Destroyed
@veryCrunchy
veryCrunchy force-pushed the feature/account-credential-slots branch from 4daca37 to f7931ce Compare September 3, 2026 21:14
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #436 / NC Native September 3, 2026 21:14 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f7931ce047

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread androidApp/src/main/kotlin/dev/obiente/nextcloudnative/AndroidPersistedSession.kt Outdated
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #436 / NC Native September 3, 2026 21:37 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0b1e4cb658

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@veryCrunchy
veryCrunchy force-pushed the feature/account-credential-slots branch from 0b1e4cb to c0526df Compare September 4, 2026 00:39
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #436 / NC Native September 4, 2026 00:39 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c0526df17f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@veryCrunchy
veryCrunchy force-pushed the feature/account-credential-slots branch from c0526df to bbc70fc Compare September 4, 2026 01:10
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #436 / NC Native September 4, 2026 01:10 Destroyed
@obiente-cloud
obiente-cloud Bot temporarily deployed to Obiente Preview / PR #436 / NC Native September 4, 2026 01:15 Destroyed

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bbc70fc564

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@veryCrunchy
veryCrunchy force-pushed the feature/account-credential-slots branch from 28965a7 to 1d310d0 Compare September 4, 2026 01:56

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 31c7c7e245

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +70 to +71
check(snapshot.malformedEntryCount == 0) {
"Reset the malformed account-removal cleanup state before signing in again."

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Provide recovery for malformed Android cleanup journals

When the journal contains even one malformed entry, startup session restoration returns null and every subsequent account selection or sign-in reaches this unconditional failure. Neither clearSession() nor the recovery worker removes or quarantines malformed entries, so the prescribed reset cannot be performed in the app and the installation remains locked out until the user wipes all app data; provide an explicit safe purge/recovery flow for this state.

AGENTS.md reference: AGENTS.md:L447-L449

Useful? React with 👍 / 👎.

val reference = requireReference(documentId, session)
if (reference.isRoot) throw SecurityException("The Nextcloud root cannot be renamed.")
val account = resolveAccount(session)
val file = findDocument(session, account, reference.path)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Avoid reacquiring the account lease during provider lookup

For every DocumentsProvider rename or delete, and for moves and creates below the root, withAndroidDocumentMutation already holds this account's non-reentrant operation mutex when findDocument() calls services.listFiles(). The newly guarded listFilesWithSource() then tries to acquire the same mutex through withRetainedAndroidAccountFileRead, so the provider call waits on its own lease before issuing DAV and never completes; perform the metadata lookup before taking the mutation lease or propagate that the lease is already held.

Useful? React with 👍 / 👎.

val reference = requireReference(documentId, session)
if (reference.isRoot) throw SecurityException("The Nextcloud root cannot be renamed.")
val account = resolveAccount(session)
val file = findDocument(session, account, reference.path)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Avoid reacquiring the Android account lease during provider lookup

For every DocumentsProvider rename, delete, or move, and for creation below a non-root folder, withAndroidDocumentMutation already holds this account's non-reentrant operation lease when findDocument() calls services.listFiles(). The newly guarded listFilesWithSource() then waits for the same lease through withRetainedAndroidAccountFileRead, so the provider operation deadlocks before issuing its DAV mutation; perform the metadata lookup before acquiring the mutation lease or propagate that the lease is already held.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eadef3dc98

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +55 to +58
synchronized(ANDROID_HOME_WORKSPACE_STORAGE_LOCK) {
val editor = preferences.edit()
keys.forEach(editor::remove)
check(editor.commit()) { "The home workspace account settings could not be removed." }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fence home-workspace writes after account retirement

When active-account removal overlaps installed-app discovery, the still-composed shell can call appPinsRepository.save after this one-shot deletion; the storage lock only orders the operations and has no retired-account generation check, so a late save recreates apps:pins and re-adding the account restores metadata that removal reported deleted. Fresh evidence beyond the earlier missing-purge report is that the added deletion still accepts writes from pre-removal UI incarnations; fence both platform storage implementations against those stale writes.

AGENTS.md reference: AGENTS.md:L327-L329

Useful? React with 👍 / 👎.

Comment on lines +15 to +18
fun stateFor(session: NextcloudSession): PhotoTimelineUiState {
val accountKey = previewCacheDigest(session)
accountStates.remove(accountKey)?.let { existing ->
accountStates[accountKey] = existing

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Put photo timeline state behind the retirement gate

When account removal overlaps composition of the old account shell, stateFor neither consults sharedAccountPrivateMemoryGate nor synchronizes with removeAccount, so a call after the purge can recreate the stable account key and retain the removed account's timeline and backup-status state for a later same-account sign-in. Fresh evidence beyond the earlier memory-cache findings is that this newly extracted repository remains outside the incarnation fence used by the adjacent Calendar, Activity, and Talk caches.

AGENTS.md reference: AGENTS.md:L327-L329

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a64f83b249

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

listOf(
{ fenceAndroidDynamicApiStateForRemoval(cacheIdentity, dynamicApiState.coalescer, dynamicApiState.cache) },
{ dynamicDiscoveryCache.retireAccount(session.accountId.storageKey, cacheIdentity) },
{ removeSupportAccount(accountIdentity) },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Remove account diagnostic history during cleanup

When an Android account has recorded support events, this callback removes support-intake submissions only; the shared AndroidSupportDiagnostics instance still retains the account's events in memory and in events-v1.jsonl. Because JvmSupportDiagnostics.visibleEvents() derives a stable scope from the account identity, signing into the same server/login again makes the removed account's old operations and timestamps visible and exportable. Add account-targeted diagnostic-history deletion with stale-publication fencing before clearing the removal journal; the ROADMAP account-removal gate includes metadata.

AGENTS.md reference: AGENTS.md:L40-L41

Useful? React with 👍 / 👎.

providerPreferenceAccountId = currentAccountId
val key = virtualFileProviderPreferenceKey(currentAccountId)
providerWasEnabledBeforeRemoval = preferences.getBoolean(key, false)
setDesktopVirtualFileProviderPreference(preferences, currentAccountId, enabled = false)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Journal provider enablement before disabling it

If the desktop process exits after this preference flush but before credential removal commits, the account remains in the registry while providerWasEnabledBeforeRemoval is lost with the process. This can occur before the cleanup journal is even prepared; on restart restoreVirtualFileProviderIfEnabled() sees the persisted false value, so a previously enabled Linux or Windows virtual-file provider never returns. Persist the prior enablement as recovery state, or defer this durable preference change until the credential-removal commit.

AGENTS.md reference: AGENTS.md:L327-L329

Useful? React with 👍 / 👎.

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