dumbtv
dumbtv가 ADB로 Fire TV에서 광고·추적·업데이트 알림을 제거, 루트·앱 변경 없이, 무료 오픈소스
자체 호스팅 오픈소스 소프트웨어로 데이터 전달마다 서명된 출처를 기록해 수신자가 자체 환경에서 경로를 검증할 수 있습니다
Self-hosted open-source software that records signed provenance at every data handoff, so recipients can verify the recorded path in their own environment.
보안TypeScript · JavaScriptGitHub @provin-line온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
DID-grant auth provider + reference policy verifier for the provin / dPLaaX stack — TS monorepo of scaffold generators and composition modules (GHCR images)
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Authentication and authorization stack for the provin wire profile of the
dPLaaX protocol: libraries plus scaffold generators that produce per-deployment
composition roots of auth.provider and auth.policy-verifier.
See docs/requirements.md for what this repository provides. See upstream compatibility before adopting current auth-family candidate builds in an existing generated deployment.
Lineage: this repository's history starts at the public cut, not at the start of the work. The code grew up in a private PoC auth stack for dPLaaS, and was carried over when the project moved to the dplaax protocol namespace (
did:dplaax, the DID grant — nowhttps://dplaax.dev/oauth/grant-type/did) and the@provin-linenpm scope. That predecessor was retired rather than published, so there is no upstream repository to link to; the earliest commit here is the snapshot the public line begins from. See CHANGELOG.md for what each release since then contains.
This repository does not operate services. Each dPLaaX deployment generates its own composition roots with the scaffold generators (docs/create-app.md):
| Generator | Generates | Default port |
|---|---|---|
| create-auth-provider | auth.provider 0.18 app with the DID grant and a did:dplaax registry resolver | HTTP_PORT, 8080 by default (the published image and the dev composition set 3000) |
| create-policy-verifier | Scope-based ABAC policy engine | 3001 |
| Package | Description |
|---|---|
| packages/did-grant | @provin-line/auth-did-grant — the DID grant as an auth.provider 0.18 module |
| packages/dplaax-registry | @provin-line/auth-dplaax-registry — resolves did:dplaax owner DIDs against a DID registry |
| packages/did-grant-store | @provin-line/auth-did-grant-store — keeps the DID grant's spent nonces on the kernel's store |
| packages/did-dplaax | @provin-line/did-dplaax — the did:dplaax method library |
| packages/policy-verifier-dplaax-module | dPLaaX collectors for the policy-verifier |
| packages/create-provider, packages/create-policy-verifier | The generators |
This repo is a pnpm monorepo:
auth/
├── packages/ # Libraries + scaffold generators (npm)
├── integration/ # Cross-package integration tests (private)
└── instances/ # Generated dev instances (git-ignored; `make instances`)
Use pnpm install at the repo root to bootstrap all workspaces.
make instances # generate dev composition roots into instances/
docker compose up
pnpm install # bootstrap the workspace
pnpm -r test # unit + integration tests
make instances # regenerate dev instances from the templates
make smoke # build, typecheck, boot + health-check both instances
Client
-> auth-provider instance (DID auth -> JWT)
-> gRPC service with protobuf.interceptors
-> policy-verifier instance (POST /verify -> allow/deny)
The DID grant (@provin-line/auth-did-grant,
resolving through @provin-line/auth-dplaax-registry)
issues tokens under a P0 auth contract (dplaax.spec). This section documents
what actually ships today, not the full target contract; the package READMEs
hold the settings and every answer.
| Contract id | Status | Notes |
|---|---|---|
LEGACY_DID_LOGIN@1 | Default, active | Relationship-blind (no authentication/assertionMethod check); controller-matched key selection only; capped by legacy-max-ttl |
OWNER_AUTHENTICATION_LOGIN@1 | Active, wired | Fork-Y: the signing key must be string-referenced in the DID Document's authentication array |
OWNER_ASSERTION_CONTROL_LOGIN@1 | Active, wired | The signing key must be string-referenced in the DID Document's assertionMethod array |
The contract is the did-grant.auth-contract setting. An OWNER_* contract
runs the OWNER validation path against the request:
login-transcript-v1 transcript: all eleven fields required and
non-empty, domain_separation_tag pinned to dplaax-owner-login-v1. A
request whose signed payload is not a valid transcript, the LEGACY message
shape included, is refused. The transcript's did and subject_did must
agree.verification_method must be string-referenced in the relationship the
contract names; an embedded method object never satisfies it (rule
auth.forky.authentication-login). It must be the method whose key
verified the signature.kid, the
transcript's verification_method and the resolver-selected method id
must agree (rule auth.grant.kid-match). This needs a JWS protected
header, so ed25519_raw and ed25519_prehash are refused at boot for an
OWNER contract.audience is one of the eleven fields, so an OWNER
request that omits it is refused. A minted OWNER token always carries
aud.issuer and
token_endpoint must be this provider's: issuer.url, and the token
endpoint's location on http.public-origin. An OWNER contract does not
boot where the token endpoint has no public location.An OWNER_* contract also needs did-grant.owner-migration-ratified = true
(rule auth.migration.enable-gate). A DID Document with more than one
verificationMethod whose controller is the authenticating DID is not yet
supported on the OWNER path: key selection refuses it as ambiguous before the
relationship check runs. Genuine multi-key OWNER selection is follow-up work.
Every minted token carries these six claims:
| Claim | Value |
|---|---|
auth_contract_id | The configured auth-contract |
verification_method | The selected verificationMethod's id: the OWNER-certified method on the OWNER path, the controller-matched method on LEGACY |
did_document_snapshot | sha256:<64-hex>: digest of the exact bytes the registry served for the DID Document |
lifecycle_state_ref | registry:<origin>#<digest>: a stable pointer to that exact resolution snapshot |
lifecycle_freshness_ref | RFC 3339 UTC instant the resolution was performed |
authorization_scope | Always AUTHORIZATION_AT_ISSUANCE_WITH_MAX_AGE@1, the only scope this module mints |
They are signed into the JWT through auth.provider's added claims. Tokens
minted at /oauth/token are bound to the client that endpoint authenticated
(RFC 9068 §2.2): client_id carries its id, taken from the authenticated
client, never from the request body. That binding is what lets the client
revoke the token through RFC 7009 /oauth/revoke. azp is not written; aud
carries the audience.
lifecycle_state_ref / lifecycle_freshness_ref are a documented P0
projection, the registry snapshot digest plus the retrieval instant,
standing in until a real lifecycle service exists. There is no live freshness
cache behind them.
The did-grant section (package README):
| Key | Required / Default | Notes |
|---|---|---|
revocation-latency-bound | Required, no default | access-token-lifetime must be at most this bound, or boot is refused |
access-token-lifetime | Required, no default | The lifetime of the grant's tokens; for LEGACY also at most legacy-max-ttl |
legacy-max-ttl | Default 15m | Cap for LEGACY_DID_LOGIN@1 tokens |
auth-contract | Default LEGACY_DID_LOGIN@1 | See Contract ids above |
owner-migration-ratified | Default false | Must be true before an OWNER_* contract boots |
supported-algorithms | Default ["ed25519_raw"] | Under an OWNER_* contract every entry must carry a JWS header (ed25519_jws, es256_jws, or a contributed one that says so) |
message-max-age | Default 5m | How far a message's timestamp may be from this host's clock |
Which audiences and scopes a client may be issued is the client's
client-allowance (grants must list the DID grant type), not a setting of
the grant. The dplaax-registry section holds url (required),
allowed-registries, timeout and max-document-size
(package README).
The generated provider ships secure-by-default settings: its
config/application.conf reads each from an environment variable and holds no
values (.env.example names them), and boot is refused without the two
required lifetimes.
Audience-absent requests (LEGACY path only, intentional). A LEGACY message
may omit audience: the token then gets the audience the client's allowance
issues by default (client-allowance's allowedIssuance). The spec's audience-required rule binds the strict OWNER profile, which
fails closed on a missing audience; LEGACY was never bound by it.
The registry module resolves through auth.provider's outbound, so its
destination policy and limits apply to every resolution:
5s (outbound.timeout, narrowed by timeout)outbound.max-response-size, narrowed
by max-document-size), enforced while streamingoutbound.max-concurrent, shared with every
outbound caller); a request past it is refused at oncecanon.strict-decode): duplicate object keys,
trailing data, invalid UTF-8 and unpaired surrogate escapes are refused;
integers beyond 2^53 are kept exactly; unknown members, __proto__
included, are preserved as own data propertiesid must equal the
requested DID, with no normalizationtemporarily_unavailable (INDETERMINATE: the DID may still
be valid; a client can retry).invalid_grant (FAILED). Never 401, which would ask the client to
authenticate.lifecycle_state_ref /
lifecycle_freshness_ref are a snapshot-at-resolution-time projection, not
a live freshness service; any spec rule that expects a positive,
continuously-refreshed liveness signal is vacuously satisfied (nothing
claims fresher than "resolved at this instant").AUTHORIZATION_AT_ISSUANCE_WITH_MAX_AGE@1 is ever minted. Spec
rules that bind a claim to "current authorization as of the request" are
satisfied by this module simply never minting that kind of claim at P0
(the spec's CURRENT_AUTHORIZATION_AT_REQUEST@1 scope is not implemented
here).Cross-repo E2E scenarios live in provin-line/e2e
(public). They run every node against cmd/pdpstub (allow-all) with a fixed
harness bearer, so they cover the node's credential gate and wireauth-signed
peer calls — never JWT issuance or a policy-decision deny. The real
three-layer auth stack (auth.provider + o3co policy-verifier) is exercised by
provin-line/oss's deploy/quickstart, which is today's only working
verification path for auth integration.
Apache-2.0. Copyright 2026 1o1 Co. Ltd.