Skip to content

Development#1

Merged
Alims-Repo merged 16 commits into
mainfrom
development
May 26, 2026
Merged

Development#1
Alims-Repo merged 16 commits into
mainfrom
development

Conversation

@Alims-Repo
Copy link
Copy Markdown
Owner

No description provided.

Alims-Repo added 16 commits May 26, 2026 20:41
… catalog

- Move publishing coordinates to root gradle.properties (GROUP, VERSION_NAME)
  so CI/tag-driven releases can override without editing build scripts.
- Register coroutines, androidx.security-crypto, robolectric libraries.
- Register mavenPublish, dokka and binary-compatibility-validator plugins
  in libs.versions.toml (single source of truth for plugin versions).
- Use catalog plugin aliases for mavenPublish, dokka and binary-compat-validator
  (no hard-coded versions in build scripts).
- Read group/version from root gradle.properties via Provider API.
- Enable Kotlin explicitApi = Strict and add -Xexpect-actual-classes /
  -opt-in compiler args to keep the public surface deliberate.
- Pin Android JVM target to 17.
- Rename iOS framework to PascalCase SecureVaultKit and mark it static so
  consumer apps link a single binary.
- Move dependencies to typed source-set DSL; drop redundant kotlin-stdlib
  (KGP adds it implicitly). Expose coroutines as api on commonMain since
  the public API uses suspend / Flow.
- Configure vanniktech publishing for KMP with JavadocJar.Dokka + sourcesJar
  and call signAllPublications(); drop the bespoke signing {} block.
- Fix invalid groupId (io.github.alims-repo -> io.github.alimsrepo),
  fill the previously empty POM description, add issueManagement and
  distribution=repo on the license.
- Remove Platform.kt stub triplet; the real public API lands next.
Replace the placeholder expect fun platform() with the real library contract:

- SecureVault interface: suspend put/get/remove/contains/clear/keys with
  documented @throws and a coroutine-first shape.
- VaultException sealed hierarchy: InvalidKey, CryptoFailure, Tampered,
  StorageUnavailable. Lets common code catch all storage errors uniformly
  without depending on java.* or platform.Security.*.
- VaultConfig immutable data class + Accessibility enum modelling Keychain
  kSecAttrAccessible* semantics, with a namespace regex validated at
  construction time (fail-fast).
- SecureVaultFactory expect class - the single platform entry point that
  hides Context / Service constructor differences from common code.
- Internal requireValidKey() helper so both backends enforce the same
  key contract.

Every public symbol is documented with KDoc and explicit visibility (the
module compiles with explicitApi = Strict). The Android and iOS actuals
follow in the next commits.
- SecureVaultFactory.android: actual class with Context constructor; stores
  applicationContext only to avoid leaking Activity/Fragment contexts.
- AndroidSecureVault: internal implementation using Jetpack Security's
  EncryptedSharedPreferences with AES-256 SIV keys + AES-256 GCM values,
  wrapped by an Android Keystore master key (AES256_GCM scheme).
- Lazy, mutex-guarded prefs initialisation so constructors stay non-blocking
  and concurrent first-use callers cannot race the file creation.
- Per-namespace file via the secure_vault__<namespace> prefix, isolating
  our data from any other SharedPreferences the host app uses.
- All blocking I/O dispatched on Dispatchers.IO; platform exceptions
  (AEADBadTagException, GeneralSecurityException, IOException) are mapped
  to VaultException.{Tampered, CryptoFailure, StorageUnavailable} via a
  small inline runCatchingStorage helper so java.* never leaks across the
  library boundary.
- SecureVaultFactory.ios: zero-arg actual class; the Keychain is process-wide
  so no platform handle is needed at construction time.
- IosSecureVault: internal implementation using SecItem{Add,Update,Copy,Delete}
  with kSecClassGenericPassword and per-namespace kSecAttrService isolation.
- Accessibility maps onto kSecAttrAccessible{AfterFirstUnlock,WhenUnlocked}
  ThisDeviceOnly so secrets never sync via iCloud Keychain or migrate to a
  different device.
- Queries are constructed as NSMutableDictionary then toll-free-bridged to
  CFDictionaryRef; kSec* string constants are bridged via NSCopying for use
  as dictionary keys. All blocking calls dispatched on Dispatchers.Default.
- OSStatus codes are translated to VaultException subtypes: errSecDecode ->
  Tampered, errSecDuplicateItem -> CryptoFailure, anything else ->
  StorageUnavailable. errSecItemNotFound is treated as a normal absent
  result rather than an error.
- put() performs SecItemUpdate first and falls back to SecItemAdd on
  errSecItemNotFound — Keychain has no native upsert.
Adds the IosSecureVault class that was missed from the previous commit.
Uses SecItem{Add,Update,Copy,Delete} on kSecClassGenericPassword items,
scoped per VaultConfig.namespace via kSecAttrService.

- Accessibility maps to kSecAttrAccessible{AfterFirstUnlock,WhenUnlocked}
  ThisDeviceOnly so secrets never sync via iCloud or migrate across devices.
- Queries are NSMutableDictionary instances toll-free bridged to
  CFDictionaryRef; kSec* constants are bridged via NSCopying for use as
  dictionary keys.
- All blocking calls dispatched on Dispatchers.Default.
- OSStatus codes translate to VaultException subtypes:
    errSecDecode        -> Tampered
    errSecDuplicateItem -> CryptoFailure
    other failures      -> StorageUnavailable
  errSecItemNotFound is a normal absent-result, not an error.
- put() performs SecItemUpdate first and falls back to SecItemAdd on
  errSecItemNotFound, since Keychain has no native upsert.
- FakeSecureVault: internal, mutex-guarded in-memory implementation kept in
  commonTest so it never enters the published API surface.
- SecureVaultContractTest: abstract behavioural contract every backend must
  satisfy (round-trip, overwrite, contains/remove idempotency, keys snapshot,
  clear, blank-key rejection). Subclassing keeps the assertions in one
  place; the iOS Keychain backend will plug into the same harness once
  simulator tests are wired up.
- FakeSecureVaultTest: instantiates the fake against the contract.
- VaultConfigTest: regression tests for the namespace regex and default
  Accessibility.

Android Robolectric tests are intentionally deferred until AGP's
com.android.kotlin.multiplatform.library plugin exposes a stable
host-test surface; tracked for v0.2.
- Replace the IDE-template README with a library-focused one: badges,
  60-second install snippet, supported targets table, Android + iOS usage,
  VaultException handling guide, threading notes, build/publish commands.
- Add LICENSE (Apache-2.0) at the repo root so vanniktech-maven-publish and
  Maven Central pick it up alongside the POM <licenses> block.
- Add CHANGELOG.md following Keep a Changelog 1.1.0 + SemVer; seed it with
  the 0.1.0 entry, including explicit "known limitations" so consumers
  know what is and isn't in scope.
- Add SECURITY.md with a responsible-disclosure policy, supported-version
  matrix, and an honest threat-model section that names the OS guarantees
  we inherit and the things SecureVault explicitly does *not* defend
  against.
- Add an .editorconfig pinning Kotlin formatting (4-space indent, LF,
  trailing newline, 120-col max, trailing commas allowed) so contributors
  and CI see the same style.
Setting `group`/`version` eagerly at the top of the build script finalises
the maven-publish plugin's `groupId`/`version` properties before the
`mavenPublishing { coordinates(...) }` block can configure them, causing:

    The value for extension 'mavenPublishing' property 'groupId$plugin'
    is final and cannot be changed any further.

Vanniktech's coordinates() already propagates the values to
`project.group` / `project.version`, so the top-level assignment is
redundant. Removing it restores configuration ordering and the build
script now scripts correctly.
Even after removing the top-level group/version assignment, the
mavenPublishing { coordinates(...) } call still fails on AGP 9.x:

  property 'groupId$plugin' is final and cannot be changed any further

Root cause: AGP's com.android.kotlin.multiplatform.library plugin
finalises the maven-publish groupId Property as a side-effect of
registering the AAR publication, before our script reaches
coordinates(...).

Fix: drop coordinates(...) entirely. vanniktech-maven-publish reads
GROUP / POM_ARTIFACT_ID / VERSION_NAME from gradle.properties and
applies them via plugin convention — i.e. *before* any other plugin
can finalise the Property. Adds POM_ARTIFACT_ID=secure-vault to root
gradle.properties and replaces the in-script coordinates(...) block
with a comment explaining why.
Single-file, dependency-free landing page at docs/index.html, ready to be
served from the repo's GitHub Pages "/docs on main" source.

- Dark, modern theme with gradient hero, sticky blurred navbar, custom SVG
  brand mark; Inter + JetBrains Mono via Google Fonts.
- Sections: hero with one-line pitch and primary/secondary CTAs, tabbed
  install card (Kotlin DSL / version catalog / Groovy DSL), why-secure-vault
  feature grid (OS-native crypto, coroutine-first, sealed errors, namespaces,
  tiny API, ABI-stable), Android+iOS usage cards styled as IDE tabs, API
  table, supported-targets matrix, footer with Maven Central / changelog /
  security links.
- Vanilla JS only: tab switching + copy-to-clipboard for the install
  snippet. No build step, no bundler, no framework — drop-in for Pages.
- Inline minimal Kotlin/Swift syntax highlighting via .tk-* token classes
  so the page stays one file with zero runtime deps.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new :secure-vault Kotlin Multiplatform library module that provides a coroutine-first SecureVault API backed by native secure storage on Android (EncryptedSharedPreferences) and iOS (Keychain), along with publishing and documentation assets.

Changes:

  • Added the secure-vault KMP module with common API (SecureVault, VaultConfig, VaultException) and Android/iOS implementations.
  • Added Maven Central publishing configuration (Gradle + GitHub Actions) and ABI validation tooling.
  • Added project documentation (README, SECURITY policy, changelog, GitHub Pages docs site).

Reviewed changes

Copilot reviewed 26 out of 27 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
settings.gradle.kts Includes the new :secure-vault module in the build.
SECURITY.md Adds a responsible disclosure policy and threat model notes.
secure-vault/src/commonMain/kotlin/io/github/alimsrepo/secure/vault/SecureVault.kt Defines the cross-platform secure storage interface.
secure-vault/src/commonMain/kotlin/io/github/alimsrepo/secure/vault/SecureVaultFactory.kt Declares the expect factory entry point for platform implementations.
secure-vault/src/commonMain/kotlin/io/github/alimsrepo/secure/vault/VaultConfig.kt Adds namespace/accessibility configuration for vault instances.
secure-vault/src/commonMain/kotlin/io/github/alimsrepo/secure/vault/VaultException.kt Introduces a sealed exception hierarchy for cross-platform error handling.
secure-vault/src/commonMain/kotlin/io/github/alimsrepo/secure/vault/internal/Validation.kt Centralizes key validation shared by all backends.
secure-vault/src/androidMain/kotlin/io/github/alimsrepo/secure/vault/SecureVaultFactory.android.kt Android actual factory implementation (Context-backed).
secure-vault/src/androidMain/kotlin/io/github/alimsrepo/secure/vault/AndroidSecureVault.kt Android EncryptedSharedPreferences-backed vault implementation.
secure-vault/src/androidMain/AndroidManifest.xml Adds the Android manifest for the library module.
secure-vault/src/iosMain/kotlin/io/github/alimsrepo/secure/vault/SecureVaultFactory.ios.kt iOS actual factory implementation (no handle required).
secure-vault/src/iosMain/kotlin/io/github/alimsrepo/secure/vault/IosSecureVault.kt iOS Keychain-backed vault implementation and CF/NS bridging helpers.
secure-vault/src/commonTest/kotlin/io/github/alimsrepo/secure/vault/SecureVaultContractTest.kt Adds shared behavioral contract tests for vault implementations.
secure-vault/src/commonTest/kotlin/io/github/alimsrepo/secure/vault/FakeSecureVault.kt Adds an in-memory implementation for contract testing.
secure-vault/src/commonTest/kotlin/io/github/alimsrepo/secure/vault/FakeSecureVaultTest.kt Runs the contract against the in-memory fake.
secure-vault/src/commonTest/kotlin/io/github/alimsrepo/secure/vault/VaultConfigTest.kt Tests VaultConfig validation and defaults.
secure-vault/build.gradle.kts Configures KMP targets, dependencies, and Maven Central publishing.
secure-vault/.gitignore Ignores the module build output.
gradle/libs.versions.toml Adds versions/libs/plugins for publishing, Dokka, BCV, coroutines, security-crypto.
gradle.properties Adds publishing coordinates and adjusts Gradle caching settings.
.github/workflows/publish.yml Adds a tag-triggered Maven Central publishing workflow.
README.md Replaces template content with library usage/installation docs.
docs/index.html Adds a static docs/landing page for GitHub Pages.
CHANGELOG.md Adds initial changelog entries for v0.1.0.
LICENSE Adds Apache-2.0 license file.
build.gradle.kts Adds the Android lint plugin alias at the root.
.editorconfig Adds repository-wide formatting/editor conventions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread gradle.properties
Comment on lines +18 to +20
GROUP=io.github.alims-repo
POM_ARTIFACT_ID=secure-vault
VERSION_NAME=0.1.0
Comment thread gradle.properties
Comment on lines +7 to +8
org.gradle.configuration-cache=false
org.gradle.caching=false
Comment on lines +33 to +35
run: |
export ORG_GRADLE_PROJECT_signingInMemoryKey=$(echo "$SIGNING_KEY_B64" | base64 -d)
./gradlew :secure-vault:publishAllPublicationsToMavenCentralRepository --no-configuration-cache
Comment on lines +179 to +183
throw when (this) {
errSecDecode -> VaultException.Tampered(RuntimeException(msg))
errSecDuplicateItem -> VaultException.CryptoFailure(RuntimeException(msg))
else -> VaultException.StorageUnavailable(RuntimeException(msg))
}
Comment on lines +39 to +47
public expect class SecureVaultFactory {

/**
* Builds a [SecureVault] for the given [config]. Multiple calls with the
* same [VaultConfig.namespace] return independent instances that address
* the same underlying storage.
*/
public fun create(config: VaultConfig): SecureVault
}
@Alims-Repo Alims-Repo merged commit 907ef2c into main May 26, 2026
3 of 4 checks passed
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.

2 participants