SDKs
DigiFIR ships two client SDKs, kept in the backend repository and generated from the same OpenAPI contract. The four Flutter apps talk to the backend only through the Dart SDK. The citizen web portal and the officials' console use the TypeScript SDK. Both implement the same canonical encoding, and both are tested against the cross-language test vectors shared with the chaincode, byte for byte.
| Dart SDK | TypeScript SDK | |
|---|---|---|
| Package | digifir_sdk (version 0.2.0), in sdk/dart | @digifir/sdk, in sdk/ts |
| Used by | Citizen, police, court and lab apps | Citizen web portal, officials' console |
| Runs on | Android, iOS, desktop and web; pure Dart with no Flutter dependency | ES2022 browsers with WebCrypto and IndexedDB, in a secure (HTTPS) context |
| Device key | PlatformKeySigner: Android Keystore or StrongBox, iPhone Secure Enclave | WebCryptoSigner: a P-256 key the browser cannot export, kept in IndexedDB |
| Tests (last full run) | 509 | 295 |
What each SDK provides
| Area | Dart | TypeScript |
|---|---|---|
| Typed client for every operation | DigifirApi | createApi |
| Transport headers, attestation, problem details as typed errors | ApiTransport, DigifirApiException | built into the client |
| Sessions by signed challenge, refresh, resume, logout | SessionManager | SessionManager (token in memory only) |
Building and signing actions, private payloads, privateHash, relay and commit wait | ActionBuilder, PrivatePayload, SignedAction, ActionRelay | Actions, signAction, sealPayload |
| Encrypted offline queue of signed actions, sent in order | Outbox | |
| Signed private reads, with an encrypted cache | PrivateReader | PrivateReads |
| Server-sent events with resume and long-poll fallback | EventStreamClient | EventStream |
| File encryption (AES-256-GCM), resumable upload, verified download | FileTransfer | encryptFile, Files |
| Identity proofing, DID registration, officials' enrolment, registrar checks | CitizenProofing, AssistedVerificationDesk, OfficialEnrolment | Identity (DigiLocker with PKCE, assisted verification) |
| Linking the desktop console with the phone | DesktopConsole | |
| Canonical JSON, NFC, SHA-256, ES256 | serialize, verifyEs256 | from @digifir/canonical |
Using the Dart SDK
Add it as a git dependency, pinned to a release tag:
dependencies:
digifir_sdk:
git:
url: https://github.com/<org>/digifir-backend.git
path: sdk/dart
ref: sdk-dart-v0.2.0Create one client per app:
final client = DigifirClient(
baseUrl: Uri.parse('https://api.pilot.digifir.gov.in'),
app: 'police-app/1.8.0', // selects the app's error codes and push channel
installId: installId, // a UUID made at first run, kept in secure storage
signer: await KeystoreSigner.load(channel),
secureStore: const SecureStorage(FlutterSecureStorage()),
blobStore: DirectoryBlobStore(Directory(appSupportPath)),
attestation: (r) => integrity.token(requestHash: r.bodySha256),
language: 'hi',
);
await client.signIn();Small secrets (the session ID, pending tokens, the keys of the outbox and read cache) go to the secure store. Records sealed with those keys go to the blob store. Nothing personal is written to the phone in clear.
Using the TypeScript SDK
import { createDigifirClient, requestPersistentStorage } from '@digifir/sdk';
export const sdk = await createDigifirClient({
baseUrl: import.meta.env.VITE_API_ORIGIN,
app: `citizen-portal/${import.meta.env.VITE_VERSION}`,
installId: getInstallId(),
lang: () => currentLang(),
audience: import.meta.env.VITE_API_ORIGIN,
attestation: (operation) => grecaptcha.enterprise.execute(SITE_KEY, { action: operation }),
});
void requestPersistentStorage(); // the device key is the only credentialThe bundle adds about 80 kB (24 kB gzipped). The bearer token goes only to operations that need it, and a 401 SESSION_EXPIRED refreshes the session once and repeats the call.
CAUTION
A browser's device key is the citizen's only credential on the portal. If the browser evicts site storage, the key is gone and the citizen must prove their identity again. The portal asks for persistent storage for this reason. On a shared computer, the backend refuses a portal key that has been idle for 30 minutes.
Sources: digifir-backend sdk/dart/README.md, sdk/ts/README.md, docs/PROJECT-STATUS.md, docs/architecture/BACKEND.md section 2.2.