8 min read

Agent authority after Mac sleep and wake

Define agent authority after Mac sleep with clear rules for lock, lid close, wake, vault access, session approvals, and overnight runs.

Agent authority after Mac sleep and wake

An approved agent process should not inherit authority merely because a Mac woke up with the same processes still in memory. Sleep, display blanking, screen lock, lid close, and wake are different operating-system events, but they all create the same security question: did the person who approved this run remain present and able to intervene?

Treat that question as an authorization decision, not as a power-management detail. If you let a coding agent hold API or SSH authority, a stale approval can turn an ordinary coffee break, commute, or overnight pause into unattended credential use. The fix is not a maze of rules. It is a small state model, a clear expiration policy, and tests that force the awkward transitions people usually skip.

Sleep is not one event

macOS distinguishes system sleep from screen sleep, and that distinction matters because a dark display does not prove that your agent stopped. Apple exposes separate NSWorkspace notifications for willSleep, didWake, screensDidSleep, and screensDidWake; the sleep and wake notifications carry no user data that tells your app why the transition occurred.

A laptop may dim or turn off its display while a build, network transfer, or local process continues. A desktop Mac can run with no active display. A notebook connected to power and peripherals can behave differently from the same notebook on battery. You cannot infer an authority decision from a pixel turning black.

Use four separate facts in your design:

  • Display state tells you whether the screen slept or woke.
  • Power state tells you whether the machine prepared for sleep or woke from it.
  • User-presence state tells you whether the session is active, locked, logged out, or switched away.
  • Vault state tells you whether secrets may be used at all.

Teams often collapse the first three into a Boolean named isAwake. That shortcut leaks authority. An agent may be running while the screen is asleep. A machine may wake to a lock screen. A user can lock the screen without sleeping the system. Each case needs its own expected result.

The right default is severe but easy to explain: a protected action needs an unlocked vault and a current approval that belongs to the current authority epoch. A wake, lock, user-session change, manual revoke, or vault lock can advance that epoch. Once it advances, calls from the old epoch fail.

Screen lock should end interactive authority

A screen lock is the clearest signal that interactive approval should stop. The Mac may continue running, and a long-lived terminal process may still have its sockets and memory, but the approving user has left the interactive session. Continuing to send API requests or SSH commands under a previous click is difficult to defend.

This does not mean every agent must die on lock. Killing local computation is a separate decision. A model can keep reading a repository, compiling code, or preparing a patch if those actions do not require a credentialed gateway. The boundary is the external action. Keep the work, remove the authority.

That distinction is useful because it avoids an all-or-nothing choice. You do not have to choose between a frozen agent and a wide-open agent. Let the agent continue with tasks that are safe in its local workspace, then make the next protected call return a clear denial:

authorization_denied
reason: authority_epoch_changed
required: unlock_vault_and_approve_session

A useful denial tells the agent what happened without leaking a secret or pretending the request failed due to networking. The agent can pause, record the blocker, and wait for the user instead of trying the same destructive command repeatedly.

Do not make an exception because the lock came from an idle timer. That is often the case where the user forgot the agent was still running. An explicit lock and an automatic lock have different human intent, but neither proves that the person remains available to approve a production change.

There is one narrow case for retaining authority across a lock: an intentionally unattended job with a separately granted, tightly limited capability. That should be a distinct run type, not a hidden exception to interactive approval. If your overnight automation behaves exactly like a daytime chat-driven agent, you have not separated the risks.

A wake event begins a new authority epoch

Wake should invalidate interactive approval even when the approved process survives. A process can pause before sleep and resume afterward with the same PID, the same environment variables, and the same open file descriptors. None of that proves the old human decision still applies.

An authority epoch is a monotonic value that marks a continuous period in which a grant may be valid. When the system crosses a boundary you care about, increment the epoch before serving another action. The process does not negotiate this. It discovers the change at its next request.

A minimal authorization record might look like this:

{
  "session_nonce": "6a018d62-2e94-4d4a-9e79-1f4e4b5ca501",
  "process_id": 84172,
  "process_start_marker": "2026-07-22T14:03:18Z",
  "signing_authority": "approved-agent-binary",
  "authority_epoch": 27,
  "approved_at": "2026-07-22T14:04:01Z",
  "per_call_approval": false
}

The process ID is only one field. macOS can reuse a PID after a process exits, so a bare PID becomes dangerous if the gateway forgets an old record and later sees the number again. Bind the approval to a fresh nonce and an observed process instance. Use the code-signing authority as an identity signal for the executable, then still require a new per-session approval for each new process run.

When your event handler sees willSleep, record the intent to revoke and immediately stop admitting new protected calls. Apple says an observer can delay sleep handling for up to 30 seconds, but do not use that allowance to complete a queue of pending actions. Deny them or mark them interrupted. The user did not authorize a last burst of work while the lid was closing.

When the machine reports wake, advance the epoch again if necessary and keep the vault gate closed until the user satisfies its unlock requirement. This handles imperfect event ordering. Power events are messy at the edges, and you should prefer a harmless extra approval over a secret-bearing call that slips through during a transition.

Lid close deserves its own test case

Closing a notebook lid feels like a sleep command to a person, but software should not assume the physical gesture maps cleanly to one operating-system event. Power source, external displays, docking gear, and settings can change the machine's behavior. The only honest answer is to test the hardware and configuration that your team actually uses.

The security policy can still be simple: lid close ends interactive authority as soon as you observe a reliable related boundary. In a normal portable setup, willSleep gives you the early chance to block new actions. If a particular configuration keeps the system awake after the lid closes, use a user-session or display boundary as a conservative fallback. Do not wait for a perfect semantic label named lidClosed; the thing you need to prevent is unattended credential use.

Run this trial with an agent holding a benign credentialed capability, such as writing a marker to a test API or running a harmless command against a disposable SSH host:

  1. Start a fresh agent process and approve its session.
  2. Confirm one protected call succeeds, then leave the agent ready to make another.
  3. Close the lid for long enough to trigger the expected power behavior, then reopen it.
  4. Without unlocking or approving again, allow the agent to retry the protected call.
  5. Confirm that the gateway denies the retry and records the authority transition before the denial.

Repeat the same trial while docked, on battery, and with an external display if people use those modes. Keep the test endpoint harmless. The purpose is to observe authority behavior, not to discover whether a production deployment can be interrupted halfway through.

A failure here often looks deceptively clean: the action log shows no call during sleep, then the first call after wake succeeds. That is still a failure if the user faced a lock screen and never approved the resumed run. The gap in time is the whole point of the test.

Display sleep is a poor revocation trigger by itself

Protect sensitive calls individually
Require Touch ID or one click for every use of keys with per-call approval enabled.

Revoking on display sleep alone is safe, but it may be too disruptive for a desktop Mac where the display sleeps during ordinary work. Keeping authority through display sleep alone is convenient, but it is also easy to get wrong when a display timeout doubles as an unattended-user signal.

Pick one rule and make the tradeoff explicit. For high-impact credentials, revoke on screen lock and system sleep, not merely on display sleep. For a machine where display sleep reliably occurs just before lock, you may choose to revoke at display sleep as an extra guard, accepting more approval prompts. The secret is not to call either choice "obvious." It depends on the deployment and what the credential can do.

Apple's separate screen and system notifications are a useful warning against assuming they mean the same thing. Monitor both, record both, and test the policy you attach to each.

A practical matrix keeps this from becoming folklore:

TransitionLocal agent workExisting session approvalVault-backed action
Display sleepsMay continueYour stated policy decidesUsually hold or allow only low-risk use
Screen locksMay continueEndDeny until fresh approval
System begins sleepPause naturallyEnd immediatelyDeny new calls
System wakes to lock screenMay resume locallyRemains endedDeny until unlock and approval
User unlocksMay continueStill endedRequire a new session approval

The user unlocks the Mac to regain their desktop. That action should not quietly restore an agent's previous authority. Unlocking a computer and approving an external action are related, but they answer different questions.

Overnight runs need a separate contract

An overnight agent run should not inherit the permissions of an afternoon interactive session. People approve interactive work while looking at the diff, the terminal, or a request card. Overnight work is an explicit decision to let something proceed without that immediate oversight.

Make the job's contract narrow enough that you can explain it in one sentence. "Run tests and prepare a pull request" is understandable. "Do whatever is needed to finish the task" is not a contract; it is a blank check.

For unattended work, separate local actions from external actions. Allow the former when they are contained: repository analysis, edits on a branch, test execution, and artifact generation. Require fresh human approval for sensitive external effects, including a production API mutation, a deployment, a package publish, a write to a shared database, or SSH access to a machine that matters.

There are cases where an overnight run must call an external service. Handle those with a dedicated credential or a test environment whose blast radius matches the job. Do not reuse an administrator credential because it already exists in the vault. The popular argument is that the person approved the agent earlier that evening. That approval covered a visible, interactive run. It does not cover whatever remains after they fall asleep.

Use a bounded action budget if the job has a legitimate recurring external task. Bound it by destination, method, and effect rather than by vague confidence scores. For example, an unattended test job might be allowed to send a fixed request to one staging endpoint, but it cannot switch hosts, change HTTP methods, or use SSH. If the job needs more, it waits.

Sallyport's per-session authorization gives each new agent process its own approval boundary. Keep overnight work in a newly started process and let its protected actions face the same scrutiny as any other unattended run.

Logs must prove denial, not just activity

Revoke a live agent run
See agent runs in the Sessions journal and revoke a session immediately when control changes.

A record that says "the Mac woke" is not proof that authority ended. You need evidence from both sides of the boundary: the operating system event that triggered a policy transition and the next protected action that the gateway refused.

macOS gives you a useful starting point for the power side of the investigation:

pmset -g log | grep -E 'Sleep|Wake|DarkWake|Display'

The exact lines vary by hardware and macOS release, but the output should show timestamped records with event names such as Sleep, Wake, DarkWake, or display transitions. Save the matching window from each trial. Do not use it as the source of truth for authorization, because it does not know your vault or your agent identity.

Your own journal should answer a different set of questions:

14:20:16.402 session_approved process=84172 epoch=27 authority=approved-agent-binary
14:22:04.118 power_will_sleep epoch=27
14:22:04.119 authority_revoked old_epoch=27 new_epoch=28 reason=system_sleep
14:25:38.771 power_did_wake epoch=28
14:25:44.025 action_denied process=84172 request=ssh.exec reason=authority_epoch_changed

The order matters. If the action appears before the revocation entry, you found a race. If no denied action appears because your test agent quietly exited, you proved nothing about a persistent process. Arrange the trial so the same long-lived process attempts a protected call after every transition.

A tamper-evident audit trail adds a second benefit: it lets you verify after the fact that the event and action sequence was not edited into a prettier story. Sallyport projects its Sessions and Activity journals from a write-blind encrypted, hash-chained audit log, and sp audit verify checks that chain offline over ciphertext. That is useful for this test because a successful verification says the recorded sequence has not been silently rewritten; it does not excuse a weak revocation policy.

Race conditions happen at the boundary

The dangerous bug is usually a request already in flight when the Mac begins to sleep or the user locks the screen. A gateway that only checks approval when it accepts a connection can let queued work execute after the authority transition. A gateway that checks only after it has injected a credential can leak the credential into a helper before it notices the revoke.

Check authority immediately before the privileged operation. That means before credential injection, before opening an SSH helper with a usable key, and before sending the HTTP request. If an operation has several privileged phases, check again at each phase where the system could otherwise cross an authority boundary.

A simple shape looks like this:

receive request
identify process and session nonce
read current authority epoch
compare request grant epoch to current epoch
check vault gate
check per-call approval when required
inject credential and execute action
append result to audit log

Keep the epoch read and the commitment to privileged execution as close together as your implementation allows. You cannot make a distributed HTTP action perfectly reversible once bytes leave the Mac. You can stop a stale grant from starting it.

Do not try to solve every edge case by delaying sleep. Apple's willSleep notification permits a short delay for handling, but blocking a laptop from sleeping to finish agent actions reverses the priority. The machine is leaving an interactive state. Revoke access, record the interruption, and let the user decide what resumes.

Per-call approval is the clean answer for keys that can cause expensive or irreversible effects. It makes sleep and wake less interesting because every use already asks the user at the moment of use. That is not a reason to skip session revocation. It is a second barrier for a smaller set of credentials.

Test the transitions people actually perform

Make the vault the boundary
A locked vault denies every action, giving credential use a hard stop at the gateway.

A good test plan does not begin with unit tests of a notification handler. Those tests help, but the failures live in real power transitions, real lock screens, and agent processes that outlast a terminal window.

Build one test agent that can wait, receive a signal, and then request a harmless protected action. Give every trial a fresh run identifier. Keep a written expectation before you run it, because otherwise a surprising outcome becomes "probably fine" after the fact.

Test at least these cases on each supported Mac configuration:

  • Lock the screen while the agent is idle, then retry an approved action before and after unlock.
  • Put the system to sleep from the Apple menu, wake it to a lock screen, and retry with the original process.
  • Let the display sleep while the system remains awake, then see whether the behavior matches your chosen display policy.
  • Close and reopen a notebook lid, both on battery and in the desk setup people use.
  • Leave an intentionally long agent run overnight, then inspect the power log, the action journal, and the first protected call after return.

Use a small result sheet for each run: expected epoch, observed power events, whether the original process survived, whether the vault was locked, and whether the first protected call was denied. That sheet catches a frequent mistake: teams test whether the app saw the event, but never test whether the action layer rejected a call afterward.

Also test the ugly timing. Start a protected action, lock the screen while it waits on a network response, then inspect whether a retry or follow-up action runs under the old grant. Start an action a fraction before sleep. Disconnect and reconnect a dock. Restart the agent process after wake and make sure it cannot borrow the old process's approval record.

You do not need a giant policy engine to make these tests pass. You need a strict vault gate, a per-process approval record, a revocable epoch, and an action path that checks them immediately before it uses a credential.

Write the policy in outcomes, then enforce it

A good authority policy fits on a page because it describes observable outcomes, not a collection of guessed operating-system labels. State what happens to protected actions after lock, sleep, wake, lid close, logout, and manual revoke. State whether local work may continue. State what the user must do to resume.

For most interactive AI coding agents, the policy should read like this:

A protected action requires an unlocked vault and an approval for the current process in the current authority epoch. Lock, sleep, user-session loss, vault lock, and manual revoke end that approval. Wake and unlock do not restore it. The agent must request a new approval before another protected action.

That rule has one cost: people approve again after returning to a Mac. Accept the cost. The alternative asks a user to remember every agent process that was alive before they closed a lid, then trust it to behave sensibly when the machine wakes hours later.

Run the tests before you call an agent setup safe for unattended work. If a request succeeds after a lock or wake without a new decision from the user, you have found authority that outlived the moment it was granted.

FAQ

Should an AI agent keep access after my Mac locks?

Treat screen lock as an authority boundary unless you have a narrow, tested reason not to. A locked desktop may still have live processes, network access, and an approved agent, but the person who granted approval is no longer present. Requiring a fresh approval after unlock is usually the safer rule for any credentialed action.

Does closing a MacBook lid always end an agent session?

Closing a laptop lid is an intent signal, not merely a display event. It often leads to sleep, but power conditions, connected displays, and settings can change what actually happens. Test the physical lid-close case on the machines you support and revoke authority at the first reliable boundary you observe.

What is the difference between display sleep and system sleep for agent security?

No. Display sleep only says the screen went dark; the Mac can remain awake and processes can keep running. System sleep means the machine entered a deeper power transition, but you still need to decide whether wake restores past authority or starts a fresh epoch.

Can an AI coding agent resume after my Mac wakes?

An agent can resume after wake only if the action layer gives it a new grant. Do not let an old approval silently carry over because the same process still exists. Resume compute work if you want, but hold credentialed calls until the user unlocks and approves again.

How should I run an AI agent overnight on a Mac?

Overnight work is safe only when the permitted work is deliberately narrower than interactive authority. Let the agent edit a local branch, run tests, or prepare a report if that fits your risk tolerance. Put deploys, production APIs, SSH access, and any credential use behind a new approval when you return.

How do I test Mac sleep and wake events?

Use pmset -g log after each trial and save the relevant lines with your own action log. It helps distinguish display events, sleep, wake, and power transitions, but it does not prove that your agent lost authority. Your action gateway must record the revocation itself and deny the next protected call.

Should agent approvals expire after a set number of minutes?

Do not use a timer as the primary expiration rule. Timeouts create failures during long builds and leave too much authority during short unattended periods. Tie revocation to observable security boundaries, then add a time limit only as a backstop for unusually long sessions.

Is a process ID enough to identify an approved agent?

A process ID alone is too weak because process IDs can be reused after exit. Bind a grant to a newly observed process instance, its code-signing authority, launch context, and a random session nonce held by the gateway. Revoke the record when the process exits or any authority boundary invalidates it.

What should an agent authorization record contain?

Store the authority epoch with every approval and every protected action. On wake, lock, logout, vault lock, or manual revoke, advance the epoch before accepting more calls. A request that carries an old epoch must fail even if its process is still alive.

How do I build a sleep and wake test plan for an AI agent?

Run the scenario with an actual long-lived agent process, an approved low-risk endpoint, and a protected endpoint that produces an obvious harmless result. Lock the screen, sleep the Mac, wake it, close the lid, and leave it overnight. For every transition, verify both the event record and the next attempted protected call; a log entry without a denial test is not enough.

Sallyport

Sallyport runs API calls and SSH commands for your AI agent. The keys stay in a local vault on your Mac; you approve each run and every action lands in a sealed journal.

© 2026 Sallyport · Open source under Apache-2.0 · Oleg Sotnikov