Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
f345e97
Phase 2 commit 1: KyteMCPToken model scaffolding
kennethphough Apr 24, 2026
6d176b9
Phase 2 commit 2: McpTokenStrategy skeleton
kennethphough Apr 24, 2026
e89ca0a
Phase 2 commit 3: McpTokenStrategy validation + tests
kennethphough Apr 24, 2026
f537ed6
Phase 2 commit 4: /mcp endpoint, first stub tool, dispatcher registra…
kennethphough Apr 24, 2026
5b9f032
Merge master (v4.3.1) — pick up DBI null-default fix for Phase 2 inte…
kennethphough Apr 25, 2026
65c7339
Restore AccountTools happy-path test now that DBI null-default is fixed
kennethphough Apr 25, 2026
bebee30
Merge master (v4.3.2) — pick up Version::get Composer-runtime read fo…
kennethphough Apr 26, 2026
7fbefa4
Phase 2 commit 5: scope enforcement on MCP tool dispatch
kennethphough Apr 26, 2026
af4c6dd
Phase 2 commit 6: controller and function read tools
kennethphough Apr 26, 2026
130c841
Phase 2 commit 7: data-model read tools
kennethphough Apr 26, 2026
b7daea0
Phase 2 commit 8: site and page read tools
kennethphough Apr 26, 2026
4bfeaed
Phase 2 commit 9: wrap list-tool returns in records
kennethphough Apr 27, 2026
a2d0532
Phase 2 commit 10: decompress bzip2 fields in read tools
kennethphough Apr 27, 2026
efa850a
Phase 2 commit 11: Origin header validation on /mcp
kennethphough Apr 27, 2026
e9bba37
Phase 2 commit 12: SaveSafeSession workaround for upstream SDK bug
kennethphough Apr 27, 2026
e347ccf
Phase 2 commit 13: log MCP_TOKEN_USE on every successful auth
kennethphough Apr 27, 2026
b85d4f1
Phase 2 commit 14: quality nits
kennethphough Apr 27, 2026
7478307
Phase 2 commit 15: token issuance + ISSUE/REVOKE logging
kennethphough Apr 27, 2026
9de4028
Phase 2 commit 16: real client IP under proxy/CDN
kennethphough Apr 27, 2026
3106f17
docs(SaveSafeSession): add CLEANUP TRACKER block with delete checklist
kennethphough Apr 27, 2026
fd74e07
Add `sensitive` flag column to Controller, DataModel, ModelAttribute
kennethphough May 21, 2026
33fb9b6
Add migration for sensitive flag columns
kennethphough May 21, 2026
dc21908
Add SensitivityPolicy runtime service + unit tests
kennethphough May 21, 2026
c50fa7b
Fix sort-order expectation in SensitivityPolicyTest
kennethphough May 21, 2026
5f5431f
Wire ActivityLogger through SensitivityPolicy
kennethphough May 21, 2026
579b6d3
Wire ErrorHandler + AI error correction through SensitivityPolicy
kennethphough May 21, 2026
37747d1
Gate MCP read tools by the sensitive flag
kennethphough May 21, 2026
bafcaf0
Strengthen CI: PHP 8.2/8.3 matrix, composer audit, PHPStan static ana…
kennethphough May 21, 2026
837e7f4
Phase 3: add JwtSessionStrategy with HS256 access tokens
kennethphough May 21, 2026
ea3e23f
Phase 3: add KyteRefreshToken model and migration
kennethphough May 21, 2026
c8a0925
Phase 3: add RefreshTokenStore (issue / rotate / revoke / revokeFamily)
kennethphough May 21, 2026
81566ff
Phase 3: add JwtEndpoint with login / refresh / logout / logout-all
kennethphough May 21, 2026
effc687
Phase 3: register JwtSessionStrategy and wire /jwt/* into Api::route()
kennethphough May 22, 2026
7319e0b
Extend MCP gating to pages via new KytePage.sensitive column
kennethphough May 21, 2026
523784c
Document SensitivityPolicy integration tests as canonical coverage
kennethphough May 22, 2026
de3425a
Generalize industry-specific language in MCP docblocks
kennethphough May 22, 2026
66a6906
Add Application.auth_mode column for JWT-aware page generation
kennethphough May 22, 2026
8aea119
fix(auth): JwtSessionStrategy must mark $api->session->hasSession=true
kennethphough May 22, 2026
aef2724
docs(CHANGELOG): add v4.4.0 entry — Phase 2 + 2.5 + 3
kennethphough May 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@v3
uses: actions/checkout@v5
- name: 'Dependency Review'
uses: actions/dependency-review-action@v3
uses: actions/dependency-review-action@v4
58 changes: 50 additions & 8 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@ permissions:
contents: read

jobs:
build:
test:
name: Unit tests (PHP ${{ matrix.php }})
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
php: ['8.2', '8.3']

services:
mariadb:
image: mariadb:10.5.29
Expand All @@ -28,30 +34,33 @@ jobs:
--health-retries=10

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Set up PHP
- name: Set up PHP ${{ matrix.php }}
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
extensions: mysqli, curl, mbstring, json
php-version: ${{ matrix.php }}
extensions: mysqli, curl, mbstring, json, bz2
coverage: none

- name: Validate composer.json and composer.lock
run: composer validate --strict

- name: Cache Composer packages
id: composer-cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
key: ${{ runner.os }}-php${{ matrix.php }}-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-php-
${{ runner.os }}-php${{ matrix.php }}-

- name: Install dependencies
run: composer install --prefer-dist --no-progress

- name: Composer audit (security advisories)
run: composer audit --no-dev

- name: Run unit tests
env:
KYTE_DB_HOST: 127.0.0.1
Expand All @@ -60,3 +69,36 @@ jobs:
KYTE_DB_DATABASE: kytedev
KYTE_DB_CHARSET: utf8
run: vendor/bin/phpunit --testsuite unit

static-analysis:
name: PHPStan static analysis
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v5

- name: Set up PHP 8.2
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
extensions: mysqli, curl, mbstring, json, bz2
coverage: none

- name: Cache Composer packages
uses: actions/cache@v5
with:
path: vendor
key: ${{ runner.os }}-php8.2-phpstan-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-php8.2-phpstan-
${{ runner.os }}-php8.2-

- name: Install dependencies
run: composer install --prefer-dist --no-progress

- name: Run PHPStan
# Level 1 with a baseline of the existing codebase's pre-existing
# findings (phpstan-baseline.neon). Going forward this enforces
# "no new level-1 violations"; the baseline shrinks as legacy
# patterns get cleaned up over time.
run: vendor/bin/phpstan analyse --memory-limit=512M --no-progress
82 changes: 82 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,85 @@
## 4.4.0

> Phase 2 (MCP server) + Phase 2.5 (sensitive-data flag) + Phase 3 (JWT auth) all ship together. **No breaking changes** — every addition is opt-in, defaults preserve v4.3.x behavior bit-for-bit. Existing customers can upgrade in place without code changes.

### New Feature: Embedded MCP server (Phase 2)

Each Kyte install can now expose a Model Context Protocol endpoint that lets AI clients (Claude Code, Claude.ai) inspect controllers, models, pages, and functions over a tenant-scoped bearer.

- **New endpoint**: `POST /mcp` — handled by `Mcp\Endpoint` outside the standard MVC pipeline. JSON-RPC over HTTP, protocol version `2025-06-18`. Bypasses Kyte's HMAC envelope.
- **New strategy**: `McpTokenStrategy` registered with the auth dispatcher. Strict `Authorization: Bearer kmcp_live_…` prefix match.
- **New model**: `KyteMCPToken` — opaque bearer tokens. Stored as sha256 hash, displayed as 16-char prefix for identification. Scoped (`read` / `draft` / `commit`), revokable, optional expiry, optional CIDR allowlist, optional application-binding.
- **New controller**: `KyteMCPTokenController` — issue, list, revoke. Generates the raw token at issuance (returned once, never recoverable). Force-overrides `kyte_account` from auth context to close a privilege-escalation vector. Emits `MCP_TOKEN_ISSUE` / `MCP_TOKEN_REVOKE` / `MCP_TOKEN_USE` / `MCP_SCOPE_VIOLATION` audit rows.
- **10 read tools shipped**: `list_applications`, `list_controllers`, `read_controller`, `list_functions`, `read_function` (with optional `version_number`), `list_models`, `read_model`, `list_sites`, `list_pages`, `read_page` (with optional `version_number`).
- **Scope enforcement**: `ScopedCallToolHandler` registered ahead of the SDK's default handler. Tools declare required scope via `#[RequiresScope('read'|'draft'|'commit')]` attribute. Fail-closed default — a tool without the attribute is unreachable.
- **Account isolation**: every tool re-asserts `entity.kyte_account === api.account.id`. Foreign ids return `null` / `[]`, never the foreign record.
- **bzip2 decompression**: `Controller.code`, `Function.code`, and `KytePageVersionContent.{html,stylesheet,javascript}` are stored compressed; `Bz2Codec::decompressIfBz2` wraps the read tools so source is returned in plaintext to the client.
- **Origin validation** on `/mcp`: per spec § Security, mitigates DNS rebinding. Policy is "no Origin → allow, Origin present → check `MCP_ALLOWED_ORIGINS` constant (CSV)". CLI clients (Claude Code) unaffected; restrictive default forces operator opt-in for browser origins.
- **Proxy-aware client IP**: `Kyte\Mcp\Util\ClientIp` reads `CF-Connecting-IP` then `X-Forwarded-For` first hop then falls back to `REMOTE_ADDR`. Gated on `KYTE_TRUST_PROXY_IP_HEADERS` constant — default-off so installs without a proxy aren't exposed to header spoofing. Wired into `McpTokenStrategy::clientIp()` for IP allowlist enforcement and audit fields.

### New Feature: Sensitive-data flag (Phase 2.5)

A three-tier opt-in flag that prevents activity/error logs from capturing request bodies, and gates MCP read tools from exposing source for flagged entities. Designed for pass-through controllers whose payload contents are regulated data the platform should not store.

- **New columns** (default `0`, no behavior change unless flipped):
- `Controller.sensitive` — blanket flag. When `1`, drops body+response from logs entirely. Handles virtual (no-model) pass-through controllers.
- `DataModel.sensitive` — same blanket treatment when the model is the request target.
- `ModelAttribute.sensitive` — per-field redaction. Distinct from existing `.protected` (which only blanks values in GET responses). Set both for both behaviors.
- `KytePage.sensitive` — MCP-only; withholds `html`/`stylesheet`/`javascript` from `read_page`. Pages don't write to activity logs.
- **New service**: `SensitivityPolicy` (`src/Core/SensitivityPolicy.php`) — single source of truth, per-request singleton with in-memory cache keyed by `(scope, name, account)`. One DB hit per tuple per request. Fail-permissive on lookup error so transient DB issues degrade to existing `SENSITIVE_FIELDS` baseline rather than to no redaction at all.
- **ActivityLogger** consults the policy before persisting `request_data` and the PUT changes diff. Blanket-sensitive → both fields null. Field-sensitive → flagged fields replaced with `[REDACTED]`, other fields pass through. The hardcoded `SENSITIVE_FIELDS` list (password / token / secret_key / etc.) still runs as a baseline on top.
- **ErrorHandler** previously captured request body AND response payload into `KyteError` with zero redaction — closed in this release. `handleException`, `handleError`, and `outputBufferCallback` all consult the policy now. AI error-correction queue (`AIErrorCorrection::queueForAnalysis`) gains its own defense-in-depth check at the top: skips any sensitive-origin row, with audit-log breadcrumb. Regulated data never reaches Anthropic for analysis.
- **MCP read tools** gated by the same flag: sensitive controller → `read_controller` returns `code: null` + `sensitive: true`; sensitive function (via parent controller) → same; sensitive model → `read_model` returns `definition: null` + `sensitive: true`; sensitive field → stripped from `definition.struct`, listed separately in `sensitive_fields`; sensitive page → content fields null. List tools (`list_controllers`, `list_models`, `list_pages`) surface a `sensitive: bool` on each row so AI clients see up front which entities are gated.
- **Runtime API responses unaffected** — a sensitive controller still returns its normal response to the caller. The flag governs log / MCP / AI exposure only, not the live contract.

### New Feature: JWT bearer authentication (Phase 3)

Modern auth path that coexists with the legacy HMAC sign/rotate. Customers can run a mix of HMAC apps and JWT apps on the same install.

- **New strategy**: `JwtSessionStrategy` — HS256 access tokens with a configurable secret, claims `{iss, sub, aud, exp, iat, nbf, jti, email, app}`. Strict `Authorization: Bearer eyJ…` prefix match (won't clash with MCP's `kmcp_live_…`). Decode + verify in `preAuth`, sets `$api->user`, `$api->account`, and `$api->session->hasSession = true` so the standard ModelController auth gate accepts the request.
- **New endpoint family**: `POST /jwt/login`, `POST /jwt/refresh`, `POST /jwt/logout`, `POST /jwt/logout-all` — handled by `JwtEndpoint` (same pattern as `Mcp\Endpoint`, bypasses the MVC pipeline so login can run pre-auth). Login posts `{email, password, app_identifier?}` and returns `{access_token, refresh_token, expires_in, token_type:'Bearer', refresh_expires_at}`.
- **Refresh tokens**: opaque (`kref_v1_…` prefix), stored as sha256 hash in new `KyteRefreshToken` table. Single-use rotation per RFC 6819 — every successful refresh revokes the presented token and issues a new one in the same family. **Reuse detection**: presenting a revoked token revokes the entire family (likely leak signal). Expiration without revocation does not trigger family kill.
- **Multilogon**: separate logins always create separate families. A user signing in from laptop and phone gets two independent families — revoking one device does not affect the other. Distinct from HMAC's `ALLOW_MULTILOGON` flag.
- **`AuthDispatcher::buildDefault()`** now registers three strategies: McpToken → JwtSession → Hmac. Each `matches()` is strict so order doesn't affect correctness; order is documented for review clarity. HMAC clients continue working unchanged via `HmacSessionStrategy`.
- **`Application.auth_mode`** column added: `'hmac'` (default) or `'jwt'`. Drives whether Shipyard's page generator emits the v1.x HMAC constructor or the v2 JWT constructor for kyte-api-js consumers.
- **Configuration constants** (define in `config.php` to enable JWT):
- `KYTE_JWT_SECRET` — required for JWT mode. At least 256 bits of entropy. Never commit to version control.
- `KYTE_JWT_ISSUER` — optional, defaults to `'kyte'`. Mismatched tokens are rejected at preAuth.
- `KYTE_JWT_ACCESS_TTL` — optional, default 900 seconds.
- `KYTE_JWT_REFRESH_TTL` — optional, default 604800 seconds (7 days).
- **Dependency**: `firebase/php-jwt ^7.0` — lightweight, well-known, no transitive deps.

### Bundled migrations

Three new SQL files in `migrations/`. Apply in order. All are additive (new columns / new table) — safe to apply ahead of code; new columns default to `0` / `'hmac'` so legacy code sees no behavior change until the matching feature is opted into.

```
migrations/4.4.0_sensitive_columns.sql # Controller / DataModel / ModelAttribute / KytePage .sensitive
migrations/4.4.0_jwt_refresh_tokens.sql # KyteRefreshToken table
migrations/4.4.0_application_auth_mode.sql # Application.auth_mode
```

### CI hardening

- **PHP matrix** in `.github/workflows/php.yml`: tests now run on PHP 8.2 AND 8.3 against MariaDB 10.5.29. Catches version-specific syntax / deprecation issues.
- **PHPStan** static analysis at level 1 with a baseline of pre-existing findings (`phpstan-baseline.neon`). New code is held to zero level-1 violations; baseline only shrinks.
- **Composer audit** step on every push — fails CI on a new advisory in production dependencies.

### Test coverage

- 180 unit tests, 531 assertions. Up from 109 at end of Phase 2.
- New test files: `SensitivityPolicyTest`, `ActivityLoggerSensitivityTest`, `ErrorHandlerSensitivityTest`, `McpSensitivityTest`, `JwtSessionStrategyTest`, `JwtEndpointTest`, `RefreshTokenStoreTest`.
- Includes a regression test for the `hasSession` integration bug surfaced during dev rollout: JwtSessionStrategy must mark `$api->session->hasSession = true` after validating the bearer, otherwise `ModelController::authenticate()` rejects every protected endpoint despite a valid JWT.

### Upgrade notes

1. Apply the three migrations above to your database.
2. If using JWT mode: define `KYTE_JWT_SECRET` in `config.php` (generate with `openssl rand -base64 48`). HMAC-only installs need no config change.
3. If you were running with `AUTH_STRATEGY_DISPATCHER='shadow'` from v4.3.0, you can now safely flip to `'on'` to activate the dispatcher. HMAC traffic routes through the new `HmacSessionStrategy` which has been shadow-verified bit-identical to the inline auth.
4. kyte-api-js v2.0+ is required on the client side for JWT apps. v1.x continues to work unchanged for HMAC apps.

---

## 4.1.1

### Bug Fix: ActivityLogger blocking exception on dynamically-loaded app models
Expand Down
12 changes: 9 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,22 @@
"description": "Light-weight framework for developing versatile PHP applications.",
"license": "MIT",
"require": {
"php": ">=7.2",
"php": ">=8.1",
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"aws/aws-sdk-php": "^3.101",
"stripe/stripe-php": "^7.2",
"dragonmantank/cron-expression": "^3.3"
"dragonmantank/cron-expression": "^3.3",
"mcp/sdk": "^0.4",
"nyholm/psr7": "^1.8",
"nyholm/psr7-server": "^1.1",
"laminas/laminas-httphandlerrunner": "^2.12",
"firebase/php-jwt": "^7.0"
},
"require-dev": {
"phpunit/phpunit": "*"
"phpunit/phpunit": "*",
"phpstan/phpstan": "^2.1"
},
"authors": [
{
Expand Down
19 changes: 19 additions & 0 deletions migrations/4.4.0_application_auth_mode.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-- =========================================================================
-- Kyte v4.4.0 - Application.auth_mode column
-- =========================================================================
-- IMPORTANT: Backup your database before running this migration.
--
-- Adds a per-Application `auth_mode` setting that Shipyard's page
-- generators consult when emitting the kyte-api-js constructor call.
--
-- 'hmac' (default) → preserves v1.x behavior; pages use HMAC sign/rotate.
-- 'jwt' → pages use the v2 JWT bearer flow (kyte-api-js >= 2.0).
--
-- Default 'hmac' means existing apps are no-op until the operator
-- explicitly opts in via the Shipyard Application form.
--
-- See src/Mvc/Model/Application.php for the model spec.
-- =========================================================================

ALTER TABLE `Application`
ADD COLUMN `auth_mode` VARCHAR(16) NOT NULL DEFAULT 'hmac';
48 changes: 48 additions & 0 deletions migrations/4.4.0_jwt_refresh_tokens.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
-- =========================================================================
-- Kyte v4.4.0 - JWT refresh token storage
-- =========================================================================
-- IMPORTANT: Backup your database before running this migration.
--
-- Creates the KyteRefreshToken table used by the JWT auth strategy. Refresh
-- tokens are opaque (NOT JWTs) and exchanged at /jwt/refresh for a fresh
-- (access_jwt, new_refresh_token) pair. Each rotation revokes the presented
-- token and issues a new one in the same `token_family` — presenting a
-- revoked token is treated as a leak signal and revokes the entire family.
--
-- See src/Mvc/Model/KyteRefreshToken.php for the model spec and reuse
-- detection rationale.
-- =========================================================================

CREATE TABLE IF NOT EXISTS `KyteRefreshToken` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,

`token_hash` VARCHAR(64) NOT NULL COMMENT 'sha256 hex of the raw refresh token',
`token_prefix` VARCHAR(32) NOT NULL COMMENT 'First chars of the raw token (kref_v1_...) for identification',
`token_family` VARCHAR(64) NOT NULL COMMENT 'Hex uuid shared by every token in a rotation chain',

`user` BIGINT UNSIGNED NOT NULL,
`application` BIGINT UNSIGNED DEFAULT NULL,

`expires_at` BIGINT UNSIGNED NOT NULL DEFAULT 0,
`last_used_at` BIGINT UNSIGNED NOT NULL DEFAULT 0,
`last_used_ip` VARCHAR(45) DEFAULT NULL,

`revoked_at` BIGINT UNSIGNED NOT NULL DEFAULT 0,
`revoked_reason` VARCHAR(64) DEFAULT NULL COMMENT 'rotated | reuse_detected | logout | admin_revoke | expired',
`rotated_to` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Successor token id when rotated; 0 while active',

`kyte_account` BIGINT UNSIGNED NOT NULL,

`created_by` BIGINT UNSIGNED DEFAULT NULL,
`date_created` BIGINT UNSIGNED DEFAULT NULL,
`modified_by` BIGINT UNSIGNED DEFAULT NULL,
`date_modified` BIGINT UNSIGNED DEFAULT NULL,
`deleted_by` BIGINT UNSIGNED DEFAULT NULL,
`date_deleted` BIGINT UNSIGNED DEFAULT NULL,
`deleted` TINYINT UNSIGNED NOT NULL DEFAULT 0,

UNIQUE KEY `idx_token_hash` (`token_hash`),
KEY `idx_token_family` (`token_family`),
KEY `idx_user` (`user`),
KEY `idx_account_expires` (`kyte_account`, `expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
40 changes: 40 additions & 0 deletions migrations/4.4.0_sensitive_columns.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
-- =========================================================================
-- Kyte v4.4.0 - Sensitive-data flag columns
-- =========================================================================
-- IMPORTANT: Backup your database before running this migration.
--
-- Adds a per-row `sensitive` boolean (0/1, default 0) to framework tables
-- that participate in the SensitivityPolicy. When set, the value gates
-- whether activity-log and error-log writers store the request body /
-- response payload, and whether MCP read tools expose source / definition /
-- content for that controller, model, field, or page. Default 0 means
-- existing installs are no-op until a flag is set.
--
-- See SensitivityPolicy::class for the runtime evaluation order:
-- 1. Controller.sensitive blanket drop (handles no-model controllers)
-- 2. DataModel.sensitive blanket drop when the model is the target
-- 3. ModelAttribute.sensitive per-field redaction
-- 4. KytePage.sensitive MCP read-only — withholds page HTML/CSS/JS
-- from read_page. Pages don't write to logs,
-- so this tier only affects MCP exposure.
--
-- ModelAttribute.sensitive is distinct from the existing
-- ModelAttribute.protected column (which only blanks values in GET
-- responses). Set both if both behaviors are desired.
-- =========================================================================

ALTER TABLE `Controller`
ADD COLUMN `sensitive` TINYINT UNSIGNED NOT NULL DEFAULT 0
AFTER `application`;

ALTER TABLE `DataModel`
ADD COLUMN `sensitive` TINYINT UNSIGNED NOT NULL DEFAULT 0
AFTER `application`;

ALTER TABLE `ModelAttribute`
ADD COLUMN `sensitive` TINYINT UNSIGNED NOT NULL DEFAULT 0
AFTER `password`;

ALTER TABLE `KytePage`
ADD COLUMN `sensitive` TINYINT UNSIGNED NOT NULL DEFAULT 0
AFTER `state`;
Loading
Loading