| 1 | # ICICI Direct controlled DEV read-only validation |
| 2 | |
| 3 | This runbook prepares and performs the first operator-controlled Breeze DEV authentication. It must not be used for orders, portfolio import, automated OTP handling, or unattended authentication. |
| 4 | |
| 5 | ## Preconditions |
| 6 | |
| 7 | - Use an authenticated DEV application user and the API gateway URL. |
| 8 | - Obtain the registered Breeze AppKey, secret key, and registered redirect URL outside Git and chat tooling. |
| 9 | - Never enter the ICICI password or OTP anywhere except the official ICICI/Breeze page. |
| 10 | - Do not run the portfolio import endpoint during this procedure. |
| 11 | |
| 12 | ## 1. Create the Kubernetes credential Secret without command-line literals |
| 13 | |
| 14 | Run in an interactive PowerShell window. Values are read without echo, converted only in memory, and sent to Kubernetes over stdin. The command prints only Kubernetes resource status. |
| 15 | |
| 16 | ```powershell |
| 17 | $Namespace = '<dev-namespace>' |
| 18 | $CredentialSecretName = 'aip-icici-direct-dev' |
| 19 | $AppKeySecure = Read-Host 'Breeze AppKey' -AsSecureString |
| 20 | $SecretKeySecure = Read-Host 'Breeze secret key' -AsSecureString |
| 21 | $appKeyPointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($AppKeySecure) |
| 22 | $secretKeyPointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecretKeySecure) |
| 23 | try { |
| 24 | $appKeyPlain = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($appKeyPointer) |
| 25 | $secretKeyPlain = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($secretKeyPointer) |
| 26 | $secretManifest = @{ |
| 27 | apiVersion = 'v1' |
| 28 | kind = 'Secret' |
| 29 | metadata = @{ name = $CredentialSecretName; namespace = $Namespace } |
| 30 | type = 'Opaque' |
| 31 | data = @{ |
| 32 | 'app-key' = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($appKeyPlain)) |
| 33 | 'secret-key' = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($secretKeyPlain)) |
| 34 | } |
| 35 | } | ConvertTo-Json -Depth 6 -Compress |
| 36 | $secretManifest | kubectl apply -f - |
| 37 | } finally { |
| 38 | [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($appKeyPointer) |
| 39 | [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($secretKeyPointer) |
| 40 | Remove-Variable appKeyPlain, secretKeyPlain, secretManifest, AppKeySecure, SecretKeySecure -ErrorAction SilentlyContinue |
| 41 | } |
| 42 | ``` |
| 43 | |
| 44 | Do not use `kubectl get secret ... -o yaml/json`, `kubectl describe secret`, or shell command-line literals containing either credential. |
| 45 | |
| 46 | ## 2. Deploy broker-service configuration |
| 47 | |
| 48 | Create an uncommitted local Helm override containing no credentials: |
| 49 | |
| 50 | ```yaml |
| 51 | iciciDirect: |
| 52 | enabled: true |
| 53 | officialDocumentationVerified: true |
| 54 | redirectUrl: "<exact redirect URL registered for the Breeze app>" |
| 55 | credentialSecretName: aip-icici-direct-dev |
| 56 | ``` |
| 57 | |
| 58 | The chart supplies these verified constants: |
| 59 | |
| 60 | - base URL: `https://api.icicidirect.com/breezeapi/api/v1/` |
| 61 | - login URL: `https://api.icicidirect.com/apiuser/login` |
| 62 | - authentication method: `breeze-interactive-session` |
| 63 | - secret reference: `ICICI_DIRECT_SECRET_KEY` |
| 64 | |
| 65 | Deploy using the normal DEV Helm upgrade with the repository values, DEV values, and the uncommitted override. Delete the local override afterward. Do not change any `ibkr` values. |
| 66 | |
| 67 | After rollout, confirm only pod readiness. Do not dump the pod environment. Before a session is attached, ICICI must report `AUTHENTICATION_REQUIRED`, never `CONNECTED`. |
| 68 | |
| 69 | ## 3. Obtain a DEV application access token |
| 70 | |
| 71 | Keep the response and token in variables so PowerShell does not render them: |
| 72 | |
| 73 | ```powershell |
| 74 | $ApiBase = 'http://localhost:13000' |
| 75 | $devLogin = Invoke-RestMethod -Method Post -Uri "$ApiBase/api/v1/auth/dev/login" ` |
| 76 | -ContentType 'application/json' -Body '{"userKey":"user-a"}' |
| 77 | $AuthorizationHeaders = @{ Authorization = "Bearer $($devLogin.accessToken)" } |
| 78 | ``` |
| 79 | |
| 80 | Do not print `$devLogin`, `$devLogin.accessToken`, or `$AuthorizationHeaders`. |
| 81 | |
| 82 | ## 4. Create the scoped ICICI connection |
| 83 | |
| 84 | ```powershell |
| 85 | $connection = Invoke-RestMethod -Method Post ` |
| 86 | -Uri "$ApiBase/api/v1/broker-connections/ICICI_DIRECT/connect" ` |
| 87 | -Headers $AuthorizationHeaders |
| 88 | $ConnectionId = $connection.connectionId |
| 89 | if ($connection.status -ne 'AUTHENTICATION_REQUIRED') { throw 'Unexpected pre-authentication state' } |
| 90 | ``` |
| 91 | |
| 92 | Reuse an existing ICICI connection owned by the same DEV user instead if appropriate. |
| 93 | |
| 94 | ## 5. Perform interactive Breeze authentication |
| 95 | |
| 96 | Initiate the connection-scoped login: |
| 97 | |
| 98 | ```powershell |
| 99 | $login = Invoke-RestMethod -Method Get ` |
| 100 | -Uri "$ApiBase/api/v1/broker-connections/$ConnectionId/icici-login" ` |
| 101 | -Headers $AuthorizationHeaders |
| 102 | $login.loginUrl |
| 103 | ``` |
| 104 | |
| 105 | Verify that the URL host is exactly `api.icicidirect.com`, then open it manually. Complete username/password and OTP only on that official page. Obtain `API_Session` according to the verified Breeze flow; do not paste it into a command line or save it in a file. |
| 106 | |
| 107 | Attach it without terminal echo or command-history disclosure: |
| 108 | |
| 109 | ```powershell |
| 110 | $ApiSessionSecure = Read-Host 'Breeze API_Session' -AsSecureString |
| 111 | $apiSessionPointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($ApiSessionSecure) |
| 112 | try { |
| 113 | $apiSessionPlain = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($apiSessionPointer) |
| 114 | $sessionBody = @{ apiSession = $apiSessionPlain } | ConvertTo-Json -Compress |
| 115 | $attached = Invoke-RestMethod -Method Post ` |
| 116 | -Uri "$ApiBase/api/v1/broker-connections/$ConnectionId/icici-session" ` |
| 117 | -Headers $AuthorizationHeaders -ContentType 'application/json' -Body $sessionBody |
| 118 | } finally { |
| 119 | [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($apiSessionPointer) |
| 120 | Remove-Variable apiSessionPlain, sessionBody, ApiSessionSecure -ErrorAction SilentlyContinue |
| 121 | } |
| 122 | if ($attached.status -ne 'CONNECTED') { throw 'Breeze session did not become CONNECTED' } |
| 123 | ``` |
| 124 | |
| 125 | Do not print `$attached` wholesale. It must not contain session material. |
| 126 | |
| 127 | ## 6. Validate reads in order |
| 128 | |
| 129 | ### Account / Customer Details |
| 130 | |
| 131 | ```powershell |
| 132 | $accounts = @(Invoke-RestMethod -Method Get ` |
| 133 | -Uri "$ApiBase/api/v1/broker-connections/$ConnectionId/accounts" ` |
| 134 | -Headers $AuthorizationHeaders) |
| 135 | $accounts | Select-Object brokerType, status, baseCurrency |
| 136 | ``` |
| 137 | |
| 138 | Expected: HTTP 200, one normalized `ICICI_DIRECT` account, `ACTIVE`, and `baseCurrency` null. Do not print display name or the masked external reference unnecessarily. |
| 139 | |
| 140 | ### Demat Holdings |
| 141 | |
| 142 | ```powershell |
| 143 | $holdings = @(Invoke-RestMethod -Method Get ` |
| 144 | -Uri "$ApiBase/api/v1/broker-connections/$ConnectionId/demat-holdings" ` |
| 145 | -Headers $AuthorizationHeaders) |
| 146 | $invalidIdentity = @($holdings | Where-Object { $_.instrument.providerInstrumentId -notmatch '^ISIN:[A-Z]{2}[A-Z0-9]{9}[0-9]$' }) |
| 147 | $fabricated = @($holdings | Where-Object { |
| 148 | $null -ne $_.instrument.exchange -or $null -ne $_.instrument.tradingCurrency -or |
| 149 | $null -ne $_.averageCost -or $null -ne $_.currentPrice -or |
| 150 | $null -ne $_.marketValue -or $null -ne $_.unrealizedProfitLoss |
| 151 | }) |
| 152 | [pscustomobject]@{ |
| 153 | holdingCount = $holdings.Count |
| 154 | invalidIdentityCount = $invalidIdentity.Count |
| 155 | fabricatedValuationCount = $fabricated.Count |
| 156 | } |
| 157 | ``` |
| 158 | |
| 159 | Expected: HTTP 200; both error counts zero. Compare quantities privately against broker-visible delivery holdings. Do not publish the complete holdings list. |
| 160 | |
| 161 | ### Funds |
| 162 | |
| 163 | ```powershell |
| 164 | $funds = @(Invoke-RestMethod -Method Get ` |
| 165 | -Uri "$ApiBase/api/v1/broker-connections/$ConnectionId/funds" ` |
| 166 | -Headers $AuthorizationHeaders) |
| 167 | $funds | Select-Object source, @{Name='currency';Expression={$_.cash.currency}}, ` |
| 168 | @{Name='amountPresent';Expression={$null -ne $_.cash.amount}}, ` |
| 169 | @{Name='settledCashAbsent';Expression={$null -eq $_.settledCash}} |
| 170 | ``` |
| 171 | |
| 172 | Expected: HTTP 200, source `BREEZE_FUNDS_TOTAL_BANK_BALANCE`, currency `INR`, amount present, and settled cash absent. Inspect the amount locally only; do not label it settled, withdrawable, or buying power. |
| 173 | |
| 174 | ### Provider capabilities |
| 175 | |
| 176 | ```powershell |
| 177 | $providers = @(Invoke-RestMethod -Method Get -Uri "$ApiBase/api/v1/brokers" -Headers $AuthorizationHeaders) |
| 178 | $icici = $providers | Where-Object brokerType -eq 'ICICI_DIRECT' |
| 179 | $icici | Select-Object status, providerStatus, capabilities, readOnly |
| 180 | ``` |
| 181 | |
| 182 | Expected connected capabilities: `ACCOUNTS_READ`, `ACCOUNT_METADATA_READ`, `POSITIONS_READ`, and `CASH_READ`. `PORTFOLIO_READ` and `ORDER_EXECUTION` must be absent; `readOnly` must be true. |
| 183 | |
| 184 | ## 7. Stop gate |
| 185 | |
| 186 | Stop after the four validations. Do not call portfolio import, broker sync as a substitute for import, order APIs, Portfolio Holdings, Portfolio Positions, or market-data APIs. |
| 187 | |
| 188 | Current ICICI Demat snapshots are not safe for Phase 5C import: account base currency, instrument exchange/trading currency, average cost, and current price are absent, while the import path and persistence schema require them. |
| 189 | |
| 190 | At the end of the operator session, remove sensitive PowerShell variables: |
| 191 | |
| 192 | ```powershell |
| 193 | Remove-Variable devLogin, AuthorizationHeaders, login, attached, accounts, holdings, funds, providers, icici -ErrorAction SilentlyContinue |
| 194 | ``` |