The FKN extension checks each capability per app and scope. Severity-0 capabilities are granted automatically. Sensitive capabilities show a consent prompt unless an existing exact or area grant covers the request. Stored grants stay on the user’s device and can be revoked from the extension dashboard.
The 30-day on-device activity log records permission decisions. It also records the first automatic or area-covered use of each capability and exact scope during a visit. Repeated calls covered by the same decision are not logged as separate entries.
As an app developer you don’t implement any of this. You call the API; the extension prompts. Your job is to ask well.
The first time a sensitive action needs an ungranted capability, the extension shows a consent sheet describing the capability, requesting app, and exact scope. The user chooses allow once, allow for this session, always allow, or deny. A denial rejects the call with an error; handle it as a normal outcome.
Pass a reason to tell the user why you are asking:
await
constframe:Frame
frame
.
locator: (selector:string) => Locator
locator('.controls')
.
getByText: (text:string) => Locator
getByText('Play')
.
click: (options?: (OperationTimeoutOptions & {
position?: {
x?: number;
y?: number;
};
}) |undefined) =>Promise<void>
click({
reason?: string |undefined
reason: 'Start playback when you press play in this app' })
ensure(operation, options?) resolves the permission an operation needs without running it. Use it to request a sensitive grant at a natural moment, such as player mount, instead of interrupting a later interaction. Severity-0 operations resolve automatically.
ensure(operation, { subtree: true }) asks for the capability on an element and everything inside it. One prompt covers all descendant locators; the prompt marks the request with a “Whole area” badge so the user knows it is broad. On the frame root it becomes the whole-page wildcard.
// One prompt: "click, everything inside .controls"
// Covered: no further prompts; the first covered use of each scope is recorded.
await
constframe:Frame
frame.
locator: (selector:string) => Locator
locator('.controls').
locator: (selector:string) => Locator
locator('#play').
click: (options?: (OperationTimeoutOptions & {
position?: {
x?: number;
y?: number;
};
}) |undefined) =>Promise<void>
click()
await
constframe:Frame
frame.
locator: (selector:string) => Locator
locator('.controls').
locator: (selector:string) => Locator
locator('#mute').
click: (options?: (OperationTimeoutOptions & {
position?: {
x?: number;
y?: number;
};
}) |undefined) =>Promise<void>
click()
The first covered use of each exact capability and scope during a visit appears in the activity log, attributed to the area grant. An exact grant or denial on a deeper element always takes precedence over a covering one.
permissions.request() accepts these current keys. The scope is usually a locator selector for frame operations or a target host for network operations.
Keys
Used for
Prompt behavior
read.text, read.info
Read text or an attribute
Prompts when not granted
read.visible, read.check, read.count
Check visibility, existence, or count
Automatic
act.click, act.type, act.hover
Click, fill, or hover
Prompts when not granted
media.video, media.appear
Control video or add filtered CSS
Automatic
media.appearU
Add CSS without filtering
Prompts when not granted
embed.iframe, embed.open
Attach or navigate a frame
Automatic
network.fetch
Cookieless cross-origin fetch
Automatic
network.fetchCredentialed, network.fetchLocal
Fetch with browser cookies or reach a local-network target
Capabilities carry a severity from 0 to 4. Severity 0 is granted automatically and recorded once per capability and scope during a visit. Higher severities prompt unless a stored, session, once, or area grant already decides the request. The catalog above is the practical source for which operations prompt.