fix(certification): align certified sharing and unsigned dedicated admission (#2610)

Integrate certification policy, SDK/Core semantics, and documentation on canonical branch. Resolve duplicated documentation and keep invalid Cloud signatures rejected.
This commit is contained in:
RockChinQ
2026-09-29 00:56:18 +08:00
committed by GitHub
parent 6515ba3b94
commit 1fde3b4283
9 changed files with 103 additions and 33 deletions
+26 -19
View File
@@ -17,7 +17,10 @@ metadata.
## Trusted issuer configuration
Configure the non-secret Ed25519 public-key ring in `data/config.yaml`:
Hosted Cloud provisions the non-secret Ed25519 issuer public-key ring out of
band. Key IDs must match the SDK envelope; values are base64 public keys, never
private signing keys. Invalid configuration fails closed; keep old issuer keys
through rotation while their signed archives remain installed.
```yaml
plugin:
@@ -39,10 +42,11 @@ PLUGIN__CERTIFICATION__TRUSTED_PUBLIC_KEYS_JSON='{"ed25519:issuer":"<base64>"}'
```
An **empty** ring is a supported state, not a misconfiguration. OSS defaults to
it, so a self-hosted instance that has not provisioned any issuer key still
installs packages (see the admission matrix below). Configure the ring to grant
the shared-runtime profile; leave it empty to keep every package on the
dedicated profile.
it and supports one Workspace; configuring certification keys on OSS is not a
supported way to enable cross-tenant sharing. Cloud provisions the ring to grant
shared placement only to eligible v2 artifacts. Certification means eligibility
for Cloud cross-tenant use of the same Worker and the same plugin/component
singleton, never a dedicated certificate or intermediate trust tier.
## Admission matrix
@@ -52,21 +56,20 @@ dedicated profile.
| Cloud | no signature | any | install on dedicated worker, without shared eligibility |
| Cloud | malformed, untrusted, invalid, or non-shared declaration | any | reject before storage with `CERTIFIED_PLUGIN_CLOUD_CERTIFICATE_INVALID`; do not treat a broken signature as unsigned |
| OSS | absent legacy envelope | any | admitted to the dedicated profile |
| OSS | valid envelope declaring `shared-runtime-v1` + `stateless-v1` | any | selected shared singleton profile |
| OSS | declaration signed by a **key this instance resolves** | false | reject with `CERTIFIED_PLUGIN_OSS_FORCE_REQUIRED` |
| OSS | declaration signed by a **key this instance resolves** | true | admitted to the dedicated profile |
| OSS | valid envelope declaring `shared-runtime-v1` + `stateless-v1` | any | dedicated only; OSS has no cross-tenant shared placement |
| OSS | invalid declaration referencing a **key this instance resolves** | false | reject with `CERTIFIED_PLUGIN_OSS_FORCE_REQUIRED` |
| OSS | invalid declaration referencing a **key this instance resolves** | true | admitted to the dedicated profile |
| OSS | declaration this instance **cannot resolve** (empty ring) | any | admitted to the dedicated profile |
The OSS row that matters for availability is the last one. Marketplace
packages are signed by the marketplace issuer and declare
`shared-runtime-v1`, while OSS ships an empty key ring by default. Treating that
as a rejection made every certified marketplace package uninstallable with
`CERTIFIED_PLUGIN_OSS_FORCE_REQUIRED` before artifact storage. Because the
certificate is signed by an issuer the instance does not declare trusted, no
shared-runtime privilege may be granted, so admission degrades the install to
the existing `oss_dev` dedicated profile and records
`CERTIFIED_PLUGIN_OSS_UNTRUSTED_DEDICATED`. This is not an escalation: it
withholds the shared profile rather than granting it.
There is no dedicated certification, dedicated signature, or intermediate
certification trust tier. Advisory review is an internal prerequisite, not an
installation trust status. An `issued` marketplace badge, source candidate,
matching version, or shared artifact/dependency tree alone does not prove Cloud
admission or Worker sharing. Compare the downloaded archive's ZIP comment,
normalized digest and signed claims with the public version record and deployed
Core trust-key ring. Confirm two Workspace bindings share one Worker PID, one
plugin object, and one object for each declared component before claiming live
cross-tenant sharing.
A declaration is "resolvable" only when its `key_id` is present in the
configured ring. A parseable declaration from a resolved key that fails
@@ -78,7 +81,11 @@ resolved certificate identity are treated as untrusted and stay dedicated.
install request carries boolean `true`. The local upload endpoint accepts the
multipart field `administrator_force=true`; GitHub and marketplace install
payloads carry the same field. The existing resource-manage authorization fence
protects those endpoints. A force never creates a Cloud dedicated fallback.
protects those endpoints. A force never creates a Cloud dedicated fallback or
certifies an invalid signature. A legacy v1 certificate may verify
cryptographically, but without a signed stateless component claim it is
rejected in Cloud; an old marketplace `issued` badge alone grants no placement.
Unsigned Cloud archives are the dedicated fallback.
## Runtime and logs
+1 -1
View File
@@ -26,7 +26,7 @@
## 插件版本要求
以下是迁移器要求的已发布 Certified Plugin 版本。旧版本即使名称相同,也不能作为当前 Cloud 迁移能力的证明。
以下是迁移器要求的插件最低版本,不是认证版本清单。已发布的专属运行版本不等于已认证可跨工作区共享;旧版本即使名称相同,也不能作为当前 Cloud 迁移能力的证明。认证须由精确制品的有效签名及 `shared-runtime-v1`、`stateless-v1` 声明证明。
- `local-agent` → `LocalAgent` **0.1.10**。
- `dify-service-api` → `DifyAgent` **0.1.10**。
+10 -1
View File
@@ -199,7 +199,7 @@ def decide_plugin_admission(
mode = DeploymentMode(deployment)
certificate = facts.certificate
if certificate.is_valid_shared_runtime:
if mode is DeploymentMode.CLOUD and certificate.is_valid_shared_runtime:
return PluginAdmissionDecision(
AdmissionDisposition.SHARED_ELIGIBLE,
AdmissionCode.SHARED_ELIGIBLE,
@@ -213,6 +213,15 @@ def decide_plugin_admission(
DEDICATED_RUNTIME,
)
# OSS has no cross-tenant shared placement; a verified claim stays dedicated
# there without inventing a dedicated certification tier.
if mode is DeploymentMode.OSS and certificate.is_valid_shared_runtime:
return PluginAdmissionDecision(
AdmissionDisposition.DEDICATED_ALLOWED,
AdmissionCode.OSS_UNTRUSTED_DEDICATED,
DEDICATED_RUNTIME,
)
if mode is DeploymentMode.CLOUD:
return PluginAdmissionDecision(
AdmissionDisposition.REJECTED, AdmissionCode.CLOUD_CERTIFICATE_INVALID, DEDICATED_RUNTIME
+3 -2
View File
@@ -345,8 +345,9 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
artifact_digest=setting.artifact_digest,
)
@staticmethod
def _execution_mode_from_setting(setting: persistence_plugin.PluginSetting) -> PluginExecutionMode:
def _execution_mode_from_setting(self, setting: persistence_plugin.PluginSetting) -> PluginExecutionMode:
if getattr(getattr(self.ap, 'deployment', None), 'mode', 'oss') != 'cloud':
return PluginExecutionMode.DEDICATED
return execution_mode_for_persisted_installation(
artifact_digest=setting.artifact_digest,
install_info=setting.install_info,
@@ -30,7 +30,7 @@ pytestmark = pytest.mark.integration
[
('cloud', 'signed_shared', False, 'shared-runtime-v1'),
('cloud', 'legacy', False, 'dedicated'),
('oss', 'signed_shared', False, 'shared-runtime-v1'),
('oss', 'signed_shared', False, 'dedicated'),
('oss', 'legacy', False, 'dedicated'),
('oss', 'forged_shared', True, 'dedicated'),
],
@@ -12,7 +12,14 @@ from langbot_plugin.entities.io.context import PluginExecutionMode
@pytest.mark.parametrize(
('deployment', 'certificate', 'certificate_id', 'force', 'expected_disposition', 'expected_code'),
[
('cloud', ('valid', 'shared-runtime-v1'), 'issuer', False, 'shared_eligible', 'CERTIFIED_PLUGIN_SHARED_ELIGIBLE'),
(
'cloud',
('valid', 'shared-runtime-v1'),
'issuer',
False,
'shared_eligible',
'CERTIFIED_PLUGIN_SHARED_ELIGIBLE',
),
('cloud', ('absent', None), None, False, 'dedicated_allowed', 'CERTIFIED_PLUGIN_UNSIGNED_DEDICATED'),
('cloud', ('absent', None), None, True, 'dedicated_allowed', 'CERTIFIED_PLUGIN_UNSIGNED_DEDICATED'),
('cloud', ('malformed', None), None, False, 'rejected', 'CERTIFIED_PLUGIN_CLOUD_CERTIFICATE_INVALID'),
@@ -25,7 +32,14 @@ from langbot_plugin.entities.io.context import PluginExecutionMode
'CERTIFIED_PLUGIN_CLOUD_CERTIFICATE_INVALID',
),
('oss', ('absent', None), None, False, 'dedicated_allowed', 'CERTIFIED_PLUGIN_OSS_LEGACY_DEDICATED'),
('oss', ('valid', 'shared-runtime-v1'), 'issuer', False, 'shared_eligible', 'CERTIFIED_PLUGIN_SHARED_ELIGIBLE'),
(
'oss',
('valid', 'shared-runtime-v1'),
'issuer',
False,
'dedicated_allowed',
'CERTIFIED_PLUGIN_OSS_UNTRUSTED_DEDICATED',
),
(
'oss',
('invalid', 'shared-runtime-v1'),
@@ -87,9 +101,7 @@ def test_admission_policy_enforces_certification_matrix(
verification=CertificateVerification(verification),
runtime_profile=runtime_profile,
component_model=(
'stateless-v1'
if verification == 'valid' and runtime_profile == 'shared-runtime-v1'
else None
'stateless-v1' if verification == 'valid' and runtime_profile == 'shared-runtime-v1' else None
),
certificate_id=certificate_id,
),
@@ -135,10 +147,48 @@ def test_legacy_shared_certificate_without_stateless_contract_is_not_shared() ->
assert decision.disposition is AdmissionDisposition.REJECTED
def test_oss_never_selects_shared_even_with_a_resolved_valid_certificate() -> None:
from langbot.pkg.plugin.certification import (
CertificateFacts,
CertificateVerification,
PluginCertificationFacts,
decide_plugin_admission,
)
facts = PluginCertificationFacts(
'installation',
'a' * 64,
CertificateFacts(
CertificateVerification.VALID,
'shared-runtime-v1',
'stateless-v1',
'issuer',
),
)
decision = decide_plugin_admission(deployment='oss', facts=facts)
assert decision.runtime_profile == 'dedicated'
assert decision.disposition.value == 'dedicated_allowed'
def test_oss_reconcile_never_reuses_persisted_shared_placement() -> None:
from types import SimpleNamespace
from langbot.pkg.plugin.connector import PluginRuntimeConnector
connector = object.__new__(PluginRuntimeConnector)
connector.ap = SimpleNamespace(deployment=SimpleNamespace(mode='oss'))
setting = SimpleNamespace(
artifact_digest='a' * 64, install_info={'_certification': _complete_persisted_certification()}
)
assert connector._execution_mode_from_setting(setting) is PluginExecutionMode.DEDICATED
def test_oss_does_not_treat_valid_nonshared_signature_as_dedicated_certification() -> None:
from langbot.pkg.plugin.certification import (
AdmissionDisposition, CertificateFacts, CertificateVerification,
PluginCertificationFacts, decide_plugin_admission,
AdmissionDisposition,
CertificateFacts,
CertificateVerification,
PluginCertificationFacts,
decide_plugin_admission,
)
decision = decide_plugin_admission(
@@ -749,6 +749,7 @@ class TestSetPluginConfig:
"""Config changes are fenced by a new runtime revision."""
get_connector_module()
connector = create_mock_connector()
connector.ap.deployment.mode = 'cloud'
configure_handler(connector, AsyncMock())
connector.handler.register_installation_binding = Mock()
+2 -1
View File
@@ -2175,7 +2175,8 @@ const zhHans = {
totpRecoveryCodesRegenerated: '已生成新的恢复代码',
totpStatusDisabled: '未启用',
totpCodesRemaining: '剩余 {{count}} 个恢复代码',
totpManagerSectionDesc: '所有者和管理员可以查看并重置本工作区账户的两步验证。',
totpManagerSectionDesc:
'所有者和管理员可以查看并重置本工作区账户的两步验证。',
revokeTotp: '重新绑定',
totpAdminResetTitle: '重新绑定 {{user}} 的两步验证',
totpAdminResetDesc:
+2 -1
View File
@@ -2172,7 +2172,8 @@ const zhHant = {
totpRecoveryCodesRegenerated: '已產生新的恢復代碼',
totpStatusDisabled: '未啟用',
totpCodesRemaining: '剩餘 {{count}} 個恢復代碼',
totpManagerSectionDesc: '擁有者與管理員可以檢視並重設本工作區帳號的兩步驗證。',
totpManagerSectionDesc:
'擁有者與管理員可以檢視並重設本工作區帳號的兩步驗證。',
revokeTotp: '重新綁定',
totpAdminResetTitle: '重新綁定 {{user}} 的兩步驗證',
totpAdminResetDesc: