S561 · SOURCE-BOUND GATE EVIDENCE
S561 · R1 uygulama: izinli uygulama başlatma akışı modeli
tam S561 implementation modülü → Operations --test hedefi ile bağlı tam focused test → ayrı Operations kaydı Bu sayfa yalnız S561 kapısına aittir; komşu kapıların kaynakları bu kabulün içine katılmaz.
S561Focused kod testiOperations id exactsource SHA exacttest target exact
operation: g8l-s561-r1-permissioned-application-launch-flow-model
uygulama/model · focused test · Operations · 3 exact excerpt
sequence-bound=true · implementation-bound=true
01 · Yürütme / doğrulama kodu
Kapının gerçek repository sözleşmesi
tam dosyaL1–L1106
kernel/src/g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s561_r1_permissioned_application_launch_flow_model.rs::S561 r1 permissioned application launch flow model implementation
#![allow(unexpected_cfgs)]
//! S561 models a permissioned application launch flow (R1 stage 4:
//! application, recovery and update demonstration).
//!
//! The model consists of an application manifest (`id`, ASCII `name` of at
//! most 32 bytes, packed `version`, requested-permission bitset over
//! {DISPLAY, INPUT, MODEM, AUDIO, STORAGE, NETWORK}, 32-byte ELF hash and a
//! trust class), a table-driven policy that maps each trust class
//! (Lab / Signed / Untrusted) to the permissions it may be granted, and a
//! six-stage launch pipeline `Requested -> PolicyChecked ->
//! CapabilitiesMinted -> Started -> Running -> Exited`. Every stage
//! transition returns a `G8lS561LaunchStepReceipt` and is folded into an
//! FNV-1a 64 trace checksum. Capabilities are minted only for granted
//! permissions, carry a deterministic id derived from the monotonic launch
//! index and the permission bit, and are all revoked when the application
//! exits. At most eight applications may be resident at once; denials are
//! recorded in a bounded denial log with their diagnostic code and the
//! requested/allowed/denied bit sets.
//!
//! Fail-closed conditions: any manifest error (zero id, empty / too long /
//! non-ASCII / non-canonical name, zero version, unknown permission bits,
//! all-zero ELF hash), permission escalation beyond the trust-class policy,
//! a duplicate id among resident applications, the ninth concurrent launch,
//! any out-of-order stage transition, an unknown application id, counter
//! overflow, an empty or oversized command script and any divergent script
//! after the first published receipt.
//!
//! What this gate does NOT claim: there is no ELF loader, no scheduler task,
//! no IPC endpoint, no real capability table and no production callsite. No
//! hardware exists for this gate; `physical observations = 0`,
//! `RUNBOOK_EXECUTED_IN_S561=NO`, Boot-to-UI physically observed = false and
//! R1 acceptance complete = false. S540 and S543 remain immutable physical
//! RED verdicts. Predecessor: S560 (modem subsystem capability supervision
//! model). Next: S562 (service kill/restart supervision model).
pub const S561_SEQUENCE: usize = 561;
pub const S561_EXPECTED_PREDECESSOR: usize = 560;
pub const S561_R1_STAGE: u8 = 4;
pub const S561_R1_RANGE_FIRST: usize = 536;
pub const S561_R1_RANGE_LAST: usize = 568;
pub const S561_SUPPORTED_PROFILE_RUNTIME_OBSERVATIONS: usize = 0;
pub const S561_PHYSICAL_OBSERVATIONS: usize = 0;
pub const S561_PHYSICAL_OR_DEVICE_OPERATIONS: usize = 0;
pub const S561_SD_WRITES: usize = 0;
pub const S561_UART_OPENS: usize = 0;
pub const S561_POWER_TRANSITIONS: usize = 0;
pub const S561_NEW_IMMUTABLE_RAW_CAPTURES: usize = 0;
pub const S561_S540_PHYSICAL_VERDICT_RETAINED_RED: bool = true;
pub const S561_S543_PHYSICAL_VERDICT_RETAINED_RED: bool = true;
pub const S561_AUTOMATIC_PROMOTION: bool = false;
pub const S561_BOOT_TO_UI_PHYSICALLY_OBSERVED: bool = false;
pub const S561_HARDWARE_PRESENT: bool = false;
pub const S561_R1_ACCEPTANCE_COMPLETE: bool = false;
pub const RUNBOOK_EXECUTED_IN_S561: bool = false;
/// Maximum number of resident (Requested..Running) applications.
pub const S561_MAX_CONCURRENT_APPLICATIONS: usize = 8;
/// Maximum manifest name length in bytes (printable ASCII only).
pub const S561_MAX_APPLICATION_NAME_BYTES: usize = 32;
/// ELF hash width carried by the manifest.
pub const S561_ELF_HASH_BYTES: usize = 32;
/// Number of distinct permissions in the bitset.
pub const S561_PERMISSION_COUNT: usize = 6;
/// Bitmask of every known permission bit.
pub const S561_ALL_PERMISSION_BITS: u8 = 0b11_1111;
/// Maximum number of commands accepted by one model service call.
pub const S561_MAX_SCRIPT_COMMANDS: usize = 64;
/// Bounded denial and exit log capacities (ring buffers).
pub const S561_DENIAL_LOG_CAPACITY: usize = 8;
pub const S561_EXIT_LOG_CAPACITY: usize = 8;
/// Number of bytes folded into the trace checksum per step receipt.
pub const S561_STEP_RECEIPT_BYTES: usize = 17;
/// FNV-1a 64-bit parameters.
pub const S561_FNV1A_OFFSET_BASIS: u64 = 0xcbf2_9ce4_8422_2325;
pub const S561_FNV1A_PRIME: u64 = 0x0000_0100_0000_01b3;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS561Permission {
Display,
Input,
Modem,
Audio,
Storage,
Network,
}
impl G8lS561Permission {
pub const ALL: [Self; S561_PERMISSION_COUNT] = [
Self::Display,
Self::Input,
Self::Modem,
Self::Audio,
Self::Storage,
Self::Network,
];
pub const fn bit(self) -> u8 {
match self {
Self::Display => 0b00_0001,
Self::Input => 0b00_0010,
Self::Modem => 0b00_0100,
Self::Audio => 0b00_1000,
Self::Storage => 0b01_0000,
Self::Network => 0b10_0000,
}
}
pub const fn index(self) -> usize {
match self {
Self::Display => 0,
Self::Input => 1,
Self::Modem => 2,
Self::Audio => 3,
Self::Storage => 4,
Self::Network => 5,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub struct G8lS561PermissionSet(u8);
impl G8lS561PermissionSet {
pub const NONE: Self = Self(0);
pub const DISPLAY: Self = Self(0b00_0001);
pub const INPUT: Self = Self(0b00_0010);
pub const MODEM: Self = Self(0b00_0100);
pub const AUDIO: Self = Self(0b00_1000);
pub const STORAGE: Self = Self(0b01_0000);
pub const NETWORK: Self = Self(0b10_0000);
pub const ALL: Self = Self(S561_ALL_PERMISSION_BITS);
/// Returns `None` when `bits` carries any unknown bit.
pub const fn from_bits(bits: u8) -> Option<Self> {
if bits & !S561_ALL_PERMISSION_BITS != 0 {
None
} else {
Some(Self(bits))
}
}
pub const fn bits(self) -> u8 {
self.0
}
pub const fn contains(self, permission: G8lS561Permission) -> bool {
self.0 & permission.bit() != 0
}
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
pub const fn difference(self, other: Self) -> Self {
Self(self.0 & !other.0)
}
pub const fn is_subset_of(self, other: Self) -> bool {
self.0 & !other.0 == 0
}
pub const fn count(self) -> u8 {
self.0.count_ones() as u8
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS561TrustClass {
Lab,
Signed,
Untrusted,
}
impl G8lS561TrustClass {
pub const fn code(self) -> u8 {
match self {
Self::Lab => 1,
Self::Signed => 2,
Self::Untrusted => 3,
}
}
/// Table-driven policy: the permissions a trust class may be granted.
pub const fn allowed_permissions(self) -> G8lS561PermissionSet {
let mut i = 0;
while i < S561_POLICY_TABLE.len() {
let (class, bits) = S561_POLICY_TABLE[i];
if class as u8 == self as u8 {
return G8lS561PermissionSet(bits);
}
i += 1;
}
G8lS561PermissionSet::NONE
}
}
/// Policy table: Lab may hold everything, Signed everything except MODEM,
/// Untrusted only DISPLAY and INPUT.
pub const S561_POLICY_TABLE: [(G8lS561TrustClass, u8); 3] = [
(G8lS561TrustClass::Lab, 0b11_1111),
(G8lS561TrustClass::Signed, 0b11_1011),
(G8lS561TrustClass::Untrusted, 0b00_0011),
];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS561ApplicationName {
bytes: [u8; S561_MAX_APPLICATION_NAME_BYTES],
len: u8,
}
impl G8lS561ApplicationName {
/// Builds a canonical name from printable ASCII; `None` when empty, too
/// long or containing a non-printable / non-ASCII byte.
pub const fn from_ascii(text: &str) -> Option<Self> {
let source = text.as_bytes();
if source.is_empty() || source.len() > S561_MAX_APPLICATION_NAME_BYTES {
return None;
}
let mut bytes = [0u8; S561_MAX_APPLICATION_NAME_BYTES];
let mut i = 0;
while i < source.len() {
if source[i] < 0x20 || source[i] > 0x7e {
return None;
}
bytes[i] = source[i];
i += 1;
}
Some(Self {
bytes,
len: source.len() as u8,
})
}
/// Raw constructor (no validation) so malformed manifests can be modelled.
pub const fn from_raw(bytes: [u8; S561_MAX_APPLICATION_NAME_BYTES], len: u8) -> Self {
Self { bytes, len }
}
pub const fn len(&self) -> usize {
self.len as usize
}
pub const fn is_empty(&self) -> bool {
self.len == 0
}
pub const fn raw_bytes(&self) -> [u8; S561_MAX_APPLICATION_NAME_BYTES] {
self.bytes
}
pub fn as_bytes(&self) -> &[u8] {
let len = core::cmp::min(self.len as usize, S561_MAX_APPLICATION_NAME_BYTES);
&self.bytes[..len]
}
pub const fn validate(&self) -> Result<(), G8lS561ApplicationLaunchFlowError> {
if self.len == 0 {
return Err(G8lS561ApplicationLaunchFlowError::ManifestEmptyName);
}
if self.len as usize > S561_MAX_APPLICATION_NAME_BYTES {
return Err(G8lS561ApplicationLaunchFlowError::ManifestNameTooLong);
}
let mut i = 0;
while i < S561_MAX_APPLICATION_NAME_BYTES {
let byte = self.bytes[i];
if i < self.len as usize {
if byte < 0x20 || byte > 0x7e {
return Err(G8lS561ApplicationLaunchFlowError::ManifestNameNotAscii);
}
} else if byte != 0 {
return Err(G8lS561ApplicationLaunchFlowError::ManifestNamePaddingNotZero);
}
i += 1;
}
Ok(())
}
}
/// Packs a semantic version as `major << 16 | minor << 8 | patch`.
pub const fn s561_pack_version(major: u8, minor: u8, patch: u8) -> u32 {
((major as u32) << 16) | ((minor as u32) << 8) | patch as u32
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS561ApplicationManifest {
pub id: u32,
pub name: G8lS561ApplicationName,
pub version: u32,
/// Raw requested bitset; unknown bits are rejected by `validate`.
pub requested_permissions: u8,
pub elf_hash: [u8; S561_ELF_HASH_BYTES],
pub trust_class: G8lS561TrustClass,
}
impl G8lS561ApplicationManifest {
pub const fn new(
id: u32,
name: G8lS561ApplicationName,
version: u32,
requested_permissions: u8,
elf_hash: [u8; S561_ELF_HASH_BYTES],
trust_class: G8lS561TrustClass,
) -> Self {
Self {
id,
name,
version,
requested_permissions,
elf_hash,
trust_class,
}
}
pub const fn validate(
&self,
) -> Result<G8lS561PermissionSet, G8lS561ApplicationLaunchFlowError> {
if self.id == 0 {
return Err(G8lS561ApplicationLaunchFlowError::ManifestZeroId);
}
if let Err(error) = self.name.validate() {
return Err(error);
}
if self.version == 0 {
return Err(G8lS561ApplicationLaunchFlowError::ManifestZeroVersion);
}
let requested = match G8lS561PermissionSet::from_bits(self.requested_permissions) {
Some(set) => set,
None => return Err(G8lS561ApplicationLaunchFlowError::ManifestUnknownPermissionBits),
};
let mut i = 0;
let mut nonzero = false;
while i < S561_ELF_HASH_BYTES {
nonzero |= self.elf_hash[i] != 0;
i += 1;
}
if !nonzero {
return Err(G8lS561ApplicationLaunchFlowError::ManifestZeroElfHash);
}
Ok(requested)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS561LaunchStage {
Requested,
PolicyChecked,
CapabilitiesMinted,
Started,
Running,
Exited,
}
impl G8lS561LaunchStage {
pub const fn code(self) -> u8 {
match self {
Self::Requested => 1,
Self::PolicyChecked => 2,
Self::CapabilitiesMinted => 3,
Self::Started => 4,
Self::Running => 5,
Self::Exited => 6,
}
}
pub const fn next(self) -> Option<Self> {
match self {
Self::Requested => Some(Self::PolicyChecked),
Self::PolicyChecked => Some(Self::CapabilitiesMinted),
Self::CapabilitiesMinted => Some(Self::Started),
Self::Started => Some(Self::Running),
Self::Running => Some(Self::Exited),
Self::Exited => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS561Capability {
pub capability_id: u64,
pub app_id: u32,
pub launch_index: u32,
pub permission: G8lS561Permission,
pub revoked: bool,
}
/// Deterministic capability id: the globally unique launch index in the
/// upper bits and the permission bit in the low byte.
pub const fn s561_capability_id(launch_index: u32, permission: G8lS561Permission) -> u64 {
((launch_index as u64) << 8) | permission.bit() as u64
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS561LaunchSlot {
pub manifest: G8lS561ApplicationManifest,
pub launch_index: u32,
pub stage: G8lS561LaunchStage,
pub granted_permissions: G8lS561PermissionSet,
pub capabilities: [Option<G8lS561Capability>; S561_PERMISSION_COUNT],
}
impl G8lS561LaunchSlot {
pub fn live_capability_count(&self) -> u8 {
self.capabilities
.iter()
.filter(|slot| matches!(slot, Some(cap) if !cap.revoked))
.count() as u8
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS561LaunchStepReceipt {
pub app_id: u32,
pub launch_index: u32,
/// `0` for the initial request, otherwise the previous stage code.
pub previous_stage_code: u8,
pub stage: G8lS561LaunchStage,
pub granted_permissions: u8,
pub capabilities_minted: u8,
pub capabilities_revoked: u8,
pub exit_code: u32,
}
impl G8lS561LaunchStepReceipt {
pub const fn encode(&self) -> [u8; S561_STEP_RECEIPT_BYTES] {
let app = self.app_id.to_le_bytes();
let launch = self.launch_index.to_le_bytes();
let exit = self.exit_code.to_le_bytes();
[
app[0],
app[1],
app[2],
app[3],
launch[0],
launch[1],
launch[2],
launch[3],
self.previous_stage_code,
self.stage.code(),
self.granted_permissions,
self.capabilities_minted,
self.capabilities_revoked,
exit[0],
exit[1],
exit[2],
exit[3],
]
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS561DenialRecord {
pub app_id: u32,
pub diagnostic_code: u64,
pub requested_permissions: u8,
pub allowed_permissions: u8,
pub denied_permissions: u8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS561ExitRecord {
pub app_id: u32,
pub launch_index: u32,
pub exit_code: u32,
pub capabilities_revoked: u8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS561LaunchCommand {
Request(G8lS561ApplicationManifest),
CheckPolicy(u32),
MintCapabilities(u32),
Start(u32),
Run(u32),
Exit { app_id: u32, exit_code: u32 },
}
/// The five commands that take a manifest from Requested to Running.
pub const fn s561_full_launch_commands(
manifest: G8lS561ApplicationManifest,
) -> [G8lS561LaunchCommand; 5] {
[
G8lS561LaunchCommand::Request(manifest),
G8lS561LaunchCommand::CheckPolicy(manifest.id),
G8lS561LaunchCommand::MintCapabilities(manifest.id),
G8lS561LaunchCommand::Start(manifest.id),
G8lS561LaunchCommand::Run(manifest.id),
]
}
pub const fn s561_fnv1a_64(seed: u64, bytes: &[u8]) -> u64 {
let mut hash = seed;
let mut i = 0;
while i < bytes.len() {
hash ^= bytes[i] as u64;
hash = hash.wrapping_mul(S561_FNV1A_PRIME);
i += 1;
}
hash
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS561ApplicationLaunchFlowError {
EmptyScript,
ScriptTooLong,
ManifestZeroId,
ManifestEmptyName,
ManifestNameTooLong,
ManifestNameNotAscii,
ManifestNamePaddingNotZero,
ManifestZeroVersion,
ManifestUnknownPermissionBits,
ManifestZeroElfHash,
DuplicateApplicationId,
ConcurrentApplicationLimit,
PermissionEscalation,
UnknownApplication,
StageOrderViolation,
CounterOverflow,
PublishedStateDrift,
}
impl G8lS561ApplicationLaunchFlowError {
pub const fn diagnostic_code(self) -> u64 {
match self {
Self::EmptyScript => 1,
Self::ScriptTooLong => 2,
Self::ManifestZeroId => 3,
Self::ManifestEmptyName => 4,
Self::ManifestNameTooLong => 5,
Self::ManifestNameNotAscii => 6,
Self::ManifestNamePaddingNotZero => 7,
Self::ManifestZeroVersion => 8,
Self::ManifestUnknownPermissionBits => 9,
Self::ManifestZeroElfHash => 10,
Self::DuplicateApplicationId => 11,
Self::ConcurrentApplicationLimit => 12,
Self::PermissionEscalation => 13,
Self::UnknownApplication => 14,
Self::StageOrderViolation => 15,
Self::CounterOverflow => 16,
Self::PublishedStateDrift => 17,
}
}
pub const ALL: [Self; 17] = [
Self::EmptyScript,
Self::ScriptTooLong,
Self::ManifestZeroId,
Self::ManifestEmptyName,
Self::ManifestNameTooLong,
Self::ManifestNameNotAscii,
Self::ManifestNamePaddingNotZero,
Self::ManifestZeroVersion,
Self::ManifestUnknownPermissionBits,
Self::ManifestZeroElfHash,
Self::DuplicateApplicationId,
Self::ConcurrentApplicationLimit,
Self::PermissionEscalation,
Self::UnknownApplication,
Self::StageOrderViolation,
Self::CounterOverflow,
Self::PublishedStateDrift,
];
}
/// The launch pipeline engine: bounded slot table, monotonic launch index,
/// bounded denial and exit logs, counters and the FNV-1a trace checksum.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS561LaunchEngine {
slots: [Option<G8lS561LaunchSlot>; S561_MAX_CONCURRENT_APPLICATIONS],
next_launch_index: u32,
denials: [Option<G8lS561DenialRecord>; S561_DENIAL_LOG_CAPACITY],
denial_count: u32,
exits: [Option<G8lS561ExitRecord>; S561_EXIT_LOG_CAPACITY],
exit_count: u32,
launches_requested: u32,
policy_checks_passed: u32,
capabilities_minted: u32,
applications_started: u32,
applications_run: u32,
capabilities_revoked: u32,
peak_concurrent: u8,
step_count: u32,
trace_checksum: u64,
}
impl G8lS561LaunchEngine {
pub const fn new() -> Self {
Self::with_next_launch_index(1)
}
/// Starts the monotonic launch index at `index` (used to model overflow).
pub const fn with_next_launch_index(index: u32) -> Self {
Self {
slots: [None; S561_MAX_CONCURRENT_APPLICATIONS],
next_launch_index: index,
denials: [None; S561_DENIAL_LOG_CAPACITY],
denial_count: 0,
exits: [None; S561_EXIT_LOG_CAPACITY],
exit_count: 0,
launches_requested: 0,
policy_checks_passed: 0,
capabilities_minted: 0,
applications_started: 0,
applications_run: 0,
capabilities_revoked: 0,
peak_concurrent: 0,
step_count: 0,
trace_checksum: S561_FNV1A_OFFSET_BASIS,
}
}
pub fn slot(&self, app_id: u32) -> Option<&G8lS561LaunchSlot> {
self.slots
.iter()
.flatten()
.find(|slot| slot.manifest.id == app_id)
}
pub fn capability(
&self,
app_id: u32,
permission: G8lS561Permission,
) -> Option<G8lS561Capability> {
self.slot(app_id)
.and_then(|slot| slot.capabilities[permission.index()])
}
pub fn active_count(&self) -> u8 {
self.slots.iter().flatten().count() as u8
}
pub const fn denial_count(&self) -> u32 {
self.denial_count
}
pub const fn denial(&self, index: usize) -> Option<G8lS561DenialRecord> {
if index < S561_DENIAL_LOG_CAPACITY {
self.denials[index]
} else {
None
}
}
pub const fn exit_count(&self) -> u32 {
self.exit_count
}
pub const fn exit_record(&self, index: usize) -> Option<G8lS561ExitRecord> {
if index < S561_EXIT_LOG_CAPACITY {
self.exits[index]
} else {
None
}
}
pub const fn next_launch_index(&self) -> u32 {
self.next_launch_index
}
pub const fn peak_concurrent(&self) -> u8 {
self.peak_concurrent
}
pub const fn step_count(&self) -> u32 {
self.step_count
}
pub const fn trace_checksum(&self) -> u64 {
self.trace_checksum
}
pub const fn counters(&self) -> [u32; 7] {
[
self.launches_requested,
self.policy_checks_passed,
self.capabilities_minted,
self.applications_started,
self.applications_run,
self.exit_count,
self.capabilities_revoked,
]
}
pub fn apply(
&mut self,
command: G8lS561LaunchCommand,
) -> Result<G8lS561LaunchStepReceipt, G8lS561ApplicationLaunchFlowError> {
match command {
G8lS561LaunchCommand::Request(manifest) => self.request(manifest),
G8lS561LaunchCommand::CheckPolicy(app_id) => self.check_policy(app_id),
G8lS561LaunchCommand::MintCapabilities(app_id) => self.mint_capabilities(app_id),
G8lS561LaunchCommand::Start(app_id) => self.start(app_id),
G8lS561LaunchCommand::Run(app_id) => self.run(app_id),
G8lS561LaunchCommand::Exit { app_id, exit_code } => self.exit(app_id, exit_code),
}
}
/// Requested: validates the manifest, rejects a duplicate resident id and
/// the ninth resident application, then occupies the first free slot.
pub fn request(
&mut self,
manifest: G8lS561ApplicationManifest,
) -> Result<G8lS561LaunchStepReceipt, G8lS561ApplicationLaunchFlowError> {
if let Err(error) = manifest.validate() {
return Err(self.deny(manifest, error, 0, 0));
}
if self.slot(manifest.id).is_some() {
return Err(self.deny(
manifest,
G8lS561ApplicationLaunchFlowError::DuplicateApplicationId,
0,
0,
));
}
let Some(free) = self.slots.iter().position(Option::is_none) else {
return Err(self.deny(
manifest,
G8lS561ApplicationLaunchFlowError::ConcurrentApplicationLimit,
0,
0,
));
};
let launch_index = self.next_launch_index;
let next = launch_index
.checked_add(1)
.ok_or(G8lS561ApplicationLaunchFlowError::CounterOverflow)?;
let launches = self
.launches_requested
.checked_add(1)
.ok_or(G8lS561ApplicationLaunchFlowError::CounterOverflow)?;
self.slots[free] = Some(G8lS561LaunchSlot {
manifest,
launch_index,
stage: G8lS561LaunchStage::Requested,
granted_permissions: G8lS561PermissionSet::NONE,
capabilities: [None; S561_PERMISSION_COUNT],
});
self.next_launch_index = next;
self.launches_requested = launches;
let active = self.active_count();
if active > self.peak_concurrent {
self.peak_concurrent = active;
}
self.record(G8lS561LaunchStepReceipt {
app_id: manifest.id,
launch_index,
previous_stage_code: 0,
stage: G8lS561LaunchStage::Requested,
granted_permissions: 0,
capabilities_minted: 0,
capabilities_revoked: 0,
exit_code: 0,
})
}
/// PolicyChecked: the requested set must be a subset of the trust-class
/// policy; escalation releases the slot and records the denied bits.
pub fn check_policy(
&mut self,
app_id: u32,
) -> Result<G8lS561LaunchStepReceipt, G8lS561ApplicationLaunchFlowError> {
let index = self.find(app_id, G8lS561LaunchStage::Requested)?;
let slot =
self.slots[index].ok_or(G8lS561ApplicationLaunchFlowError::UnknownApplication)?;
let requested = slot.manifest.validate()?;
let allowed = slot.manifest.trust_class.allowed_permissions();
if !requested.is_subset_of(allowed) {
self.slots[index] = None;
return Err(self.deny(
slot.manifest,
G8lS561ApplicationLaunchFlowError::PermissionEscalation,
allowed.bits(),
requested.difference(allowed).bits(),
));
}
self.policy_checks_passed = self
.policy_checks_passed
.checked_add(1)
.ok_or(G8lS561ApplicationLaunchFlowError::CounterOverflow)?;
let updated = G8lS561LaunchSlot {
stage: G8lS561LaunchStage::PolicyChecked,
granted_permissions: requested,
..slot
};
self.slots[index] = Some(updated);
self.record(G8lS561LaunchStepReceipt {
app_id,
launch_index: slot.launch_index,
previous_stage_code: G8lS561LaunchStage::Requested.code(),
stage: G8lS561LaunchStage::PolicyChecked,
granted_permissions: requested.bits(),
capabilities_minted: 0,
capabilities_revoked: 0,
exit_code: 0,
})
}
/// CapabilitiesMinted: one capability per granted permission, none else.
pub fn mint_capabilities(
&mut self,
app_id: u32,
) -> Result<G8lS561LaunchStepReceipt, G8lS561ApplicationLaunchFlowError> {
let index = self.find(app_id, G8lS561LaunchStage::PolicyChecked)?;
let mut slot =
self.slots[index].ok_or(G8lS561ApplicationLaunchFlowError::UnknownApplication)?;
let mut minted = 0u8;
for permission in G8lS561Permission::ALL {
if slot.granted_permissions.contains(permission) {
slot.capabilities[permission.index()] = Some(G8lS561Capability {
capability_id: s561_capability_id(slot.launch_index, permission),
app_id,
launch_index: slot.launch_index,
permission,
revoked: false,
});
minted += 1;
}
}
self.capabilities_minted = self
.capabilities_minted
.checked_add(minted as u32)
.ok_or(G8lS561ApplicationLaunchFlowError::CounterOverflow)?;
slot.stage = G8lS561LaunchStage::CapabilitiesMinted;
self.slots[index] = Some(slot);
self.record(G8lS561LaunchStepReceipt {
app_id,
launch_index: slot.launch_index,
previous_stage_code: G8lS561LaunchStage::PolicyChecked.code(),
stage: G8lS561LaunchStage::CapabilitiesMinted,
granted_permissions: slot.granted_permissions.bits(),
capabilities_minted: minted,
capabilities_revoked: 0,
exit_code: 0,
})
}
pub fn start(
&mut self,
app_id: u32,
) -> Result<G8lS561LaunchStepReceipt, G8lS561ApplicationLaunchFlowError> {
let index = self.find(app_id, G8lS561LaunchStage::CapabilitiesMinted)?;
self.applications_started = self
.applications_started
.checked_add(1)
.ok_or(G8lS561ApplicationLaunchFlowError::CounterOverflow)?;
self.advance(index, G8lS561LaunchStage::Started)
}
pub fn run(
&mut self,
app_id: u32,
) -> Result<G8lS561LaunchStepReceipt, G8lS561ApplicationLaunchFlowError> {
let index = self.find(app_id, G8lS561LaunchStage::Started)?;
self.applications_run = self
.applications_run
.checked_add(1)
.ok_or(G8lS561ApplicationLaunchFlowError::CounterOverflow)?;
self.advance(index, G8lS561LaunchStage::Running)
}
/// Exited: every live capability is revoked, the slot is released and a
/// bounded exit record is kept.
pub fn exit(
&mut self,
app_id: u32,
exit_code: u32,
) -> Result<G8lS561LaunchStepReceipt, G8lS561ApplicationLaunchFlowError> {
let index = self.find(app_id, G8lS561LaunchStage::Running)?;
let mut slot =
self.slots[index].ok_or(G8lS561ApplicationLaunchFlowError::UnknownApplication)?;
let mut revoked = 0u8;
for capability in slot.capabilities.iter_mut().flatten() {
if !capability.revoked {
capability.revoked = true;
revoked += 1;
}
}
self.capabilities_revoked = self
.capabilities_revoked
.checked_add(revoked as u32)
.ok_or(G8lS561ApplicationLaunchFlowError::CounterOverflow)?;
let exits = self
.exit_count
.checked_add(1)
.ok_or(G8lS561ApplicationLaunchFlowError::CounterOverflow)?;
slot.stage = G8lS561LaunchStage::Exited;
self.exits[(self.exit_count as usize) % S561_EXIT_LOG_CAPACITY] = Some(G8lS561ExitRecord {
app_id,
launch_index: slot.launch_index,
exit_code,
capabilities_revoked: revoked,
});
self.exit_count = exits;
self.slots[index] = None;
self.record(G8lS561LaunchStepReceipt {
app_id,
launch_index: slot.launch_index,
previous_stage_code: G8lS561LaunchStage::Running.code(),
stage: G8lS561LaunchStage::Exited,
granted_permissions: slot.granted_permissions.bits(),
capabilities_minted: 0,
capabilities_revoked: revoked,
exit_code,
})
}
fn find(
&self,
app_id: u32,
expected: G8lS561LaunchStage,
) -> Result<usize, G8lS561ApplicationLaunchFlowError> {
let index = self
.slots
.iter()
.position(|slot| matches!(slot, Some(slot) if slot.manifest.id == app_id))
.ok_or(G8lS561ApplicationLaunchFlowError::UnknownApplication)?;
match self.slots[index] {
Some(slot) if slot.stage == expected => Ok(index),
_ => Err(G8lS561ApplicationLaunchFlowError::StageOrderViolation),
}
}
fn advance(
&mut self,
index: usize,
stage: G8lS561LaunchStage,
) -> Result<G8lS561LaunchStepReceipt, G8lS561ApplicationLaunchFlowError> {
let mut slot =
self.slots[index].ok_or(G8lS561ApplicationLaunchFlowError::UnknownApplication)?;
if slot.stage.next() != Some(stage) {
return Err(G8lS561ApplicationLaunchFlowError::StageOrderViolation);
}
let previous = slot.stage;
slot.stage = stage;
self.slots[index] = Some(slot);
self.record(G8lS561LaunchStepReceipt {
app_id: slot.manifest.id,
launch_index: slot.launch_index,
previous_stage_code: previous.code(),
stage,
granted_permissions: slot.granted_permissions.bits(),
capabilities_minted: 0,
capabilities_revoked: 0,
exit_code: 0,
})
}
fn deny(
&mut self,
manifest: G8lS561ApplicationManifest,
error: G8lS561ApplicationLaunchFlowError,
allowed: u8,
denied: u8,
) -> G8lS561ApplicationLaunchFlowError {
self.denials[(self.denial_count as usize) % S561_DENIAL_LOG_CAPACITY] =
Some(G8lS561DenialRecord {
app_id: manifest.id,
diagnostic_code: error.diagnostic_code(),
requested_permissions: manifest.requested_permissions,
allowed_permissions: allowed,
denied_permissions: denied,
});
self.denial_count = self.denial_count.saturating_add(1);
error
}
fn record(
&mut self,
receipt: G8lS561LaunchStepReceipt,
) -> Result<G8lS561LaunchStepReceipt, G8lS561ApplicationLaunchFlowError> {
self.step_count = self
.step_count
.checked_add(1)
.ok_or(G8lS561ApplicationLaunchFlowError::CounterOverflow)?;
self.trace_checksum = s561_fnv1a_64(self.trace_checksum, &receipt.encode());
Ok(receipt)
}
}
impl Default for G8lS561LaunchEngine {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct G8lS561ApplicationLaunchFlowReceipt {
pub sequence: usize,
pub predecessor_sequence: usize,
pub r1_stage: u8,
pub command_count: usize,
pub step_count: u32,
pub launches_requested: u32,
pub policy_checks_passed: u32,
pub capabilities_minted: u32,
pub applications_started: u32,
pub applications_run: u32,
pub applications_exited: u32,
pub capabilities_revoked: u32,
pub denials_recorded: u32,
pub peak_concurrent_applications: u8,
pub final_resident_applications: u8,
pub max_concurrent_applications: u8,
pub trace_checksum: u64,
pub s540_physical_verdict_retained_red: bool,
pub s543_physical_verdict_retained_red: bool,
pub automatic_promotion: bool,
pub hardware_present: bool,
pub supported_profile_runtime_observations: usize,
pub physical_observations: usize,
pub runbook_executed: bool,
}
#[derive(Clone, Copy, Debug)]
pub struct G8lS561ApplicationLaunchFlowState {
engine: G8lS561LaunchEngine,
receipt: Option<G8lS561ApplicationLaunchFlowReceipt>,
}
impl G8lS561ApplicationLaunchFlowState {
pub const fn new() -> Self {
Self {
engine: G8lS561LaunchEngine::new(),
receipt: None,
}
}
pub const fn receipt(&self) -> Option<G8lS561ApplicationLaunchFlowReceipt> {
self.receipt
}
pub const fn engine(&self) -> &G8lS561LaunchEngine {
&self.engine
}
}
impl Default for G8lS561ApplicationLaunchFlowState {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum G8lS561ApplicationLaunchFlowOutcome {
Published(G8lS561ApplicationLaunchFlowReceipt),
Retained(G8lS561ApplicationLaunchFlowReceipt),
}
/// Runs a bounded command script on a fresh engine. Any denied launch,
/// stage-order violation or malformed manifest fails the whole script closed
/// and leaves the state untouched. An exact replay of the published script
/// is retained with the same receipt; any divergent script is rejected.
pub fn service_s561_model_launch_flow(
state: &mut G8lS561ApplicationLaunchFlowState,
commands: &[G8lS561LaunchCommand],
) -> Result<G8lS561ApplicationLaunchFlowOutcome, G8lS561ApplicationLaunchFlowError> {
if commands.is_empty() {
return Err(G8lS561ApplicationLaunchFlowError::EmptyScript);
}
if commands.len() > S561_MAX_SCRIPT_COMMANDS {
return Err(G8lS561ApplicationLaunchFlowError::ScriptTooLong);
}
let mut engine = G8lS561LaunchEngine::new();
for command in commands {
engine.apply(*command)?;
}
let counters = engine.counters();
let receipt = G8lS561ApplicationLaunchFlowReceipt {
sequence: S561_SEQUENCE,
predecessor_sequence: S561_EXPECTED_PREDECESSOR,
r1_stage: S561_R1_STAGE,
command_count: commands.len(),
step_count: engine.step_count(),
launches_requested: counters[0],
policy_checks_passed: counters[1],
capabilities_minted: counters[2],
applications_started: counters[3],
applications_run: counters[4],
applications_exited: counters[5],
capabilities_revoked: counters[6],
denials_recorded: engine.denial_count(),
peak_concurrent_applications: engine.peak_concurrent(),
final_resident_applications: engine.active_count(),
max_concurrent_applications: S561_MAX_CONCURRENT_APPLICATIONS as u8,
trace_checksum: engine.trace_checksum(),
s540_physical_verdict_retained_red: S561_S540_PHYSICAL_VERDICT_RETAINED_RED,
s543_physical_verdict_retained_red: S561_S543_PHYSICAL_VERDICT_RETAINED_RED,
automatic_promotion: S561_AUTOMATIC_PROMOTION,
hardware_present: S561_HARDWARE_PRESENT,
supported_profile_runtime_observations: S561_SUPPORTED_PROFILE_RUNTIME_OBSERVATIONS,
physical_observations: S561_PHYSICAL_OBSERVATIONS,
runbook_executed: RUNBOOK_EXECUTED_IN_S561,
};
if let Some(published) = state.receipt {
if published != receipt {
return Err(G8lS561ApplicationLaunchFlowError::PublishedStateDrift);
}
return Ok(G8lS561ApplicationLaunchFlowOutcome::Retained(published));
}
state.engine = engine;
state.receipt = Some(receipt);
Ok(G8lS561ApplicationLaunchFlowOutcome::Published(receipt))
}
snippet sha256: cc5be8c3b0fb…file sha256: cc5be8c3b0fb…
02 · Doğrulayan test kodu
Operations komutuna bağlı focused test
tam dosyaL1–L760
simulation/tests/g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s561_r1_permissioned_application_launch_flow_model.rs::S561 r1 permissioned application launch flow model focused tests
use aselsan_microkernel_simulation::g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s561_r1_permissioned_application_launch_flow_model::*;
use std::collections::BTreeSet;
const SOURCE: &str = include_str!(
"../../kernel/src/g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s561_r1_permissioned_application_launch_flow_model.rs"
);
const MAIN: &str = include_str!("../../kernel/src/main.rs");
const SIMULATION_LIB: &str = include_str!("../src/lib.rs");
/// Pinned FNV-1a 64 trace checksum of the reference script (two full
/// launches, one exit) computed by an independent reference implementation
/// of the 17-byte step-receipt encoding.
const REFERENCE_SCRIPT_TRACE_CHECKSUM: u64 = 0xc640_3643_8d60_dd56;
fn name(text: &str) -> G8lS561ApplicationName {
G8lS561ApplicationName::from_ascii(text).unwrap()
}
fn hash(seed: u8) -> [u8; S561_ELF_HASH_BYTES] {
let mut out = [0u8; S561_ELF_HASH_BYTES];
for (i, byte) in out.iter_mut().enumerate() {
*byte = seed.wrapping_add(i as u8);
}
out
}
fn manifest(id: u32, permissions: u8, trust: G8lS561TrustClass) -> G8lS561ApplicationManifest {
G8lS561ApplicationManifest::new(
id,
name("dialer"),
s561_pack_version(1, 2, 3),
permissions,
hash(id as u8),
trust,
)
}
fn signed_dialer() -> G8lS561ApplicationManifest {
manifest(
0x5601,
G8lS561PermissionSet::DISPLAY
.union(G8lS561PermissionSet::INPUT)
.union(G8lS561PermissionSet::AUDIO)
.bits(),
G8lS561TrustClass::Signed,
)
}
fn untrusted_viewer() -> G8lS561ApplicationManifest {
manifest(0x5602, G8lS561PermissionSet::DISPLAY.bits(), G8lS561TrustClass::Untrusted)
}
fn reference_script() -> Vec<G8lS561LaunchCommand> {
let mut script = s561_full_launch_commands(signed_dialer()).to_vec();
script.extend(s561_full_launch_commands(untrusted_viewer()));
script.push(G8lS561LaunchCommand::Exit { app_id: 0x5602, exit_code: 0 });
script
}
fn launch_to_running(engine: &mut G8lS561LaunchEngine, manifest: G8lS561ApplicationManifest) {
for command in s561_full_launch_commands(manifest) {
engine.apply(command).unwrap();
}
}
#[test]
fn sequence_scope_and_nonpromotion_are_exact() {
assert_eq!(S561_SEQUENCE, 561);
assert_eq!(S561_EXPECTED_PREDECESSOR, 560);
assert_eq!(S561_R1_STAGE, 4);
assert_eq!(S561_R1_RANGE_FIRST, 536);
assert_eq!(S561_R1_RANGE_LAST, 568);
assert_eq!(S561_SUPPORTED_PROFILE_RUNTIME_OBSERVATIONS, 0);
assert_eq!(S561_PHYSICAL_OBSERVATIONS, 0);
assert_eq!(S561_PHYSICAL_OR_DEVICE_OPERATIONS, 0);
assert_eq!(S561_SD_WRITES, 0);
assert_eq!(S561_UART_OPENS, 0);
assert_eq!(S561_POWER_TRANSITIONS, 0);
assert_eq!(S561_NEW_IMMUTABLE_RAW_CAPTURES, 0);
assert!(S561_S540_PHYSICAL_VERDICT_RETAINED_RED);
assert!(S561_S543_PHYSICAL_VERDICT_RETAINED_RED);
assert!(!S561_AUTOMATIC_PROMOTION);
assert!(!S561_BOOT_TO_UI_PHYSICALLY_OBSERVED);
assert!(!S561_HARDWARE_PRESENT);
assert!(!S561_R1_ACCEPTANCE_COMPLETE);
assert!(!RUNBOOK_EXECUTED_IN_S561);
assert_eq!(S561_MAX_CONCURRENT_APPLICATIONS, 8);
assert_eq!(S561_MAX_APPLICATION_NAME_BYTES, 32);
assert_eq!(S561_ELF_HASH_BYTES, 32);
assert_eq!(S561_PERMISSION_COUNT, 6);
assert_eq!(S561_MAX_SCRIPT_COMMANDS, 64);
}
#[test]
fn module_is_registered_in_kernel_and_simulation() {
let module = "g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s561_r1_permissioned_application_launch_flow_model";
assert!(MAIN.contains(&format!("mod {module};")));
assert!(SIMULATION_LIB.contains(&format!("pub mod {module};")));
}
#[test]
fn source_has_no_device_execution_or_uart_emission_surface() {
for forbidden in [
"unsafe",
"asm!",
"write_volatile",
"crate::uart",
"crate::arch",
"#[no_mangle]",
"spin::",
"std::",
"std::process::Command",
"/dev/disk",
"/dev/cu.",
"diskutil",
"dd if=",
"TIOCEXCL",
"crate::kprintln!",
] {
assert!(!SOURCE.contains(forbidden), "forbidden token: {forbidden}");
}
assert!(SOURCE.contains("no production callsite"));
assert!(SOURCE.contains("RUNBOOK_EXECUTED_IN_S561=NO"));
}
#[test]
fn diagnostic_codes_are_nonzero_and_unique() {
let codes: BTreeSet<_> = G8lS561ApplicationLaunchFlowError::ALL
.into_iter()
.map(G8lS561ApplicationLaunchFlowError::diagnostic_code)
.collect();
assert_eq!(codes.len(), G8lS561ApplicationLaunchFlowError::ALL.len());
assert_eq!(codes.len(), 17);
assert!(!codes.contains(&0));
assert_eq!(G8lS561ApplicationLaunchFlowError::PublishedStateDrift.diagnostic_code(), 17);
}
#[test]
fn exact_replay_retains_the_same_receipt() {
let mut state = G8lS561ApplicationLaunchFlowState::new();
let script = reference_script();
let G8lS561ApplicationLaunchFlowOutcome::Published(receipt) =
service_s561_model_launch_flow(&mut state, &script).unwrap()
else {
panic!("first publication missing")
};
assert_eq!(state.receipt(), Some(receipt));
assert_eq!(
service_s561_model_launch_flow(&mut state, &script),
Ok(G8lS561ApplicationLaunchFlowOutcome::Retained(receipt))
);
assert_eq!(state.receipt(), Some(receipt));
assert_eq!(state.engine().active_count(), 1);
}
#[test]
fn divergent_input_after_publication_fails_closed() {
let mut state = G8lS561ApplicationLaunchFlowState::new();
let script = reference_script();
service_s561_model_launch_flow(&mut state, &script).unwrap();
let before = state.receipt();
// Same counts, different exit code: only the trace checksum differs.
let mut different_exit = script.clone();
*different_exit.last_mut().unwrap() = G8lS561LaunchCommand::Exit { app_id: 0x5602, exit_code: 7 };
assert_eq!(
service_s561_model_launch_flow(&mut state, &different_exit),
Err(G8lS561ApplicationLaunchFlowError::PublishedStateDrift)
);
// One command fewer.
let shorter = &script[..script.len() - 1];
assert_eq!(
service_s561_model_launch_flow(&mut state, shorter),
Err(G8lS561ApplicationLaunchFlowError::PublishedStateDrift)
);
// Reordered launches (same multiset of commands).
let mut reordered = s561_full_launch_commands(untrusted_viewer()).to_vec();
reordered.extend(s561_full_launch_commands(signed_dialer()));
reordered.push(G8lS561LaunchCommand::Exit { app_id: 0x5602, exit_code: 0 });
assert_eq!(
service_s561_model_launch_flow(&mut state, &reordered),
Err(G8lS561ApplicationLaunchFlowError::PublishedStateDrift)
);
assert_eq!(state.receipt(), before);
}
#[test]
fn permission_bits_and_policy_table_are_exact() {
assert_eq!(G8lS561Permission::Display.bit(), 1);
assert_eq!(G8lS561Permission::Input.bit(), 2);
assert_eq!(G8lS561Permission::Modem.bit(), 4);
assert_eq!(G8lS561Permission::Audio.bit(), 8);
assert_eq!(G8lS561Permission::Storage.bit(), 16);
assert_eq!(G8lS561Permission::Network.bit(), 32);
assert_eq!(G8lS561PermissionSet::ALL.bits(), 63);
assert_eq!(G8lS561PermissionSet::ALL.count(), 6);
assert_eq!(G8lS561PermissionSet::from_bits(64), None);
assert_eq!(G8lS561PermissionSet::from_bits(0b1000_0001), None);
assert_eq!(G8lS561PermissionSet::from_bits(63).map(|s| s.bits()), Some(63));
for (i, permission) in G8lS561Permission::ALL.into_iter().enumerate() {
assert_eq!(permission.index(), i);
assert_eq!(permission.bit(), 1 << i);
}
assert_eq!(G8lS561TrustClass::Lab.allowed_permissions(), G8lS561PermissionSet::ALL);
assert_eq!(
G8lS561TrustClass::Signed.allowed_permissions(),
G8lS561PermissionSet::ALL.difference(G8lS561PermissionSet::MODEM)
);
assert_eq!(G8lS561TrustClass::Signed.allowed_permissions().bits(), 0b11_1011);
assert_eq!(
G8lS561TrustClass::Untrusted.allowed_permissions(),
G8lS561PermissionSet::DISPLAY.union(G8lS561PermissionSet::INPUT)
);
assert!(!G8lS561TrustClass::Signed.allowed_permissions().contains(G8lS561Permission::Modem));
assert!(!G8lS561TrustClass::Untrusted.allowed_permissions().contains(G8lS561Permission::Storage));
assert_eq!(S561_POLICY_TABLE.len(), 3);
assert_eq!(
[G8lS561TrustClass::Lab.code(), G8lS561TrustClass::Signed.code(), G8lS561TrustClass::Untrusted.code()],
[1, 2, 3]
);
assert_eq!(s561_pack_version(1, 2, 3), 0x0001_0203);
assert_eq!(s561_pack_version(255, 255, 255), 0x00ff_ffff);
}
#[test]
fn full_pipeline_walks_requested_to_exited_with_receipts() {
let mut engine = G8lS561LaunchEngine::new();
let app = signed_dialer();
let expected = [
(0, G8lS561LaunchStage::Requested, 0u8),
(1, G8lS561LaunchStage::PolicyChecked, 0b00_1011),
(2, G8lS561LaunchStage::CapabilitiesMinted, 0b00_1011),
(3, G8lS561LaunchStage::Started, 0b00_1011),
(4, G8lS561LaunchStage::Running, 0b00_1011),
];
for ((previous, stage, granted), command) in
expected.into_iter().zip(s561_full_launch_commands(app))
{
let receipt = engine.apply(command).unwrap();
assert_eq!(receipt.app_id, app.id);
assert_eq!(receipt.launch_index, 1);
assert_eq!(receipt.previous_stage_code, previous);
assert_eq!(receipt.stage, stage);
assert_eq!(receipt.granted_permissions, granted);
assert_eq!(engine.slot(app.id).unwrap().stage, stage);
}
let exit = engine.exit(app.id, 0x2a).unwrap();
assert_eq!(exit.previous_stage_code, 5);
assert_eq!(exit.stage, G8lS561LaunchStage::Exited);
assert_eq!(exit.stage.code(), 6);
assert_eq!(exit.exit_code, 0x2a);
assert_eq!(exit.capabilities_revoked, 3);
assert_eq!(engine.step_count(), 6);
assert_eq!(engine.counters(), [1, 1, 3, 1, 1, 1, 3]);
assert_eq!(engine.slot(app.id), None);
assert_eq!(G8lS561LaunchStage::Exited.next(), None);
assert_eq!(G8lS561LaunchStage::Requested.next(), Some(G8lS561LaunchStage::PolicyChecked));
}
#[test]
fn capabilities_are_minted_only_for_granted_permissions() {
let mut engine = G8lS561LaunchEngine::new();
let app = signed_dialer();
engine.request(app).unwrap();
assert_eq!(engine.slot(app.id).unwrap().live_capability_count(), 0);
engine.check_policy(app.id).unwrap();
assert_eq!(engine.slot(app.id).unwrap().live_capability_count(), 0);
let minted = engine.mint_capabilities(app.id).unwrap();
assert_eq!(minted.capabilities_minted, 3);
let slot = engine.slot(app.id).unwrap();
assert_eq!(slot.live_capability_count(), 3);
for permission in [G8lS561Permission::Display, G8lS561Permission::Input, G8lS561Permission::Audio] {
let capability = engine.capability(app.id, permission).unwrap();
assert_eq!(capability.capability_id, s561_capability_id(1, permission));
assert_eq!(capability.capability_id, (1u64 << 8) | permission.bit() as u64);
assert_eq!(capability.app_id, app.id);
assert_eq!(capability.launch_index, 1);
assert_eq!(capability.permission, permission);
assert!(!capability.revoked);
}
for permission in [G8lS561Permission::Modem, G8lS561Permission::Storage, G8lS561Permission::Network] {
assert_eq!(engine.capability(app.id, permission), None);
}
assert_eq!(engine.counters()[2], 3);
}
#[test]
fn lab_trust_class_is_granted_every_permission_and_zero_request_mints_nothing() {
let mut engine = G8lS561LaunchEngine::new();
let lab = manifest(0x5610, G8lS561PermissionSet::ALL.bits(), G8lS561TrustClass::Lab);
launch_to_running(&mut engine, lab);
assert_eq!(engine.slot(lab.id).unwrap().live_capability_count(), 6);
assert_eq!(engine.slot(lab.id).unwrap().granted_permissions, G8lS561PermissionSet::ALL);
let ids: BTreeSet<u64> = G8lS561Permission::ALL
.into_iter()
.map(|permission| engine.capability(lab.id, permission).unwrap().capability_id)
.collect();
assert_eq!(ids.len(), 6);
let none = manifest(0x5611, 0, G8lS561TrustClass::Untrusted);
launch_to_running(&mut engine, none);
assert_eq!(engine.slot(none.id).unwrap().live_capability_count(), 0);
assert_eq!(engine.counters()[2], 6);
let exit = engine.exit(none.id, 0).unwrap();
assert_eq!(exit.capabilities_revoked, 0);
}
#[test]
fn permission_escalation_is_denied_with_reason_and_slot_released() {
let mut engine = G8lS561LaunchEngine::new();
let requested = G8lS561PermissionSet::DISPLAY
.union(G8lS561PermissionSet::MODEM)
.union(G8lS561PermissionSet::NETWORK);
let escalating = manifest(0x5620, requested.bits(), G8lS561TrustClass::Untrusted);
engine.request(escalating).unwrap();
assert_eq!(engine.active_count(), 1);
assert_eq!(
engine.check_policy(escalating.id),
Err(G8lS561ApplicationLaunchFlowError::PermissionEscalation)
);
assert_eq!(engine.active_count(), 0);
assert_eq!(engine.slot(escalating.id), None);
assert_eq!(engine.denial_count(), 1);
assert_eq!(
engine.denial(0),
Some(G8lS561DenialRecord {
app_id: 0x5620,
diagnostic_code: 13,
requested_permissions: 0b10_0101,
allowed_permissions: 0b00_0011,
denied_permissions: 0b10_0100,
})
);
assert_eq!(engine.counters()[1], 0);
assert_eq!(engine.counters()[2], 0);
// Signed may not hold MODEM either, even when everything else is allowed.
let signed_modem = manifest(0x5621, G8lS561PermissionSet::MODEM.bits(), G8lS561TrustClass::Signed);
engine.request(signed_modem).unwrap();
assert_eq!(
engine.check_policy(signed_modem.id),
Err(G8lS561ApplicationLaunchFlowError::PermissionEscalation)
);
assert_eq!(engine.denial(1).unwrap().denied_permissions, 0b00_0100);
// Lab may hold MODEM.
let lab_modem = manifest(0x5622, G8lS561PermissionSet::MODEM.bits(), G8lS561TrustClass::Lab);
engine.request(lab_modem).unwrap();
assert!(engine.check_policy(lab_modem.id).is_ok());
assert_eq!(engine.denial_count(), 2);
// A denied launch inside a service script fails the whole script closed.
let mut state = G8lS561ApplicationLaunchFlowState::new();
assert_eq!(
service_s561_model_launch_flow(&mut state, &s561_full_launch_commands(escalating)),
Err(G8lS561ApplicationLaunchFlowError::PermissionEscalation)
);
assert_eq!(state.receipt(), None);
}
#[test]
fn manifest_errors_fail_closed_and_are_logged() {
let base = signed_dialer();
let mut long_name = [b'a'; 32];
long_name[0] = b'z';
let mut padded = [0u8; 32];
padded[..3].copy_from_slice(b"abc");
padded[31] = 1;
let mut control = [0u8; 32];
control[..3].copy_from_slice(b"a\nb");
let cases: [(G8lS561ApplicationManifest, G8lS561ApplicationLaunchFlowError); 8] = [
(
G8lS561ApplicationManifest { id: 0, ..base },
G8lS561ApplicationLaunchFlowError::ManifestZeroId,
),
(
G8lS561ApplicationManifest { name: G8lS561ApplicationName::from_raw([0; 32], 0), ..base },
G8lS561ApplicationLaunchFlowError::ManifestEmptyName,
),
(
G8lS561ApplicationManifest { name: G8lS561ApplicationName::from_raw(long_name, 33), ..base },
G8lS561ApplicationLaunchFlowError::ManifestNameTooLong,
),
(
G8lS561ApplicationManifest { name: G8lS561ApplicationName::from_raw(control, 3), ..base },
G8lS561ApplicationLaunchFlowError::ManifestNameNotAscii,
),
(
G8lS561ApplicationManifest { name: G8lS561ApplicationName::from_raw(padded, 3), ..base },
G8lS561ApplicationLaunchFlowError::ManifestNamePaddingNotZero,
),
(
G8lS561ApplicationManifest { version: 0, ..base },
G8lS561ApplicationLaunchFlowError::ManifestZeroVersion,
),
(
G8lS561ApplicationManifest { requested_permissions: 0b0100_0001, ..base },
G8lS561ApplicationLaunchFlowError::ManifestUnknownPermissionBits,
),
(
G8lS561ApplicationManifest { elf_hash: [0; 32], ..base },
G8lS561ApplicationLaunchFlowError::ManifestZeroElfHash,
),
];
let mut engine = G8lS561LaunchEngine::new();
for (i, (bad, error)) in cases.into_iter().enumerate() {
assert_eq!(bad.validate(), Err(error));
assert_eq!(engine.request(bad), Err(error));
assert_eq!(engine.active_count(), 0);
assert_eq!(engine.step_count(), 0);
assert_eq!(engine.denial_count() as usize, i + 1);
let denial = engine.denial(i).unwrap();
assert_eq!(denial.diagnostic_code, error.diagnostic_code());
assert_eq!(denial.app_id, bad.id);
assert_eq!(denial.requested_permissions, bad.requested_permissions);
}
// Ninth denial wraps the bounded ring onto index 0.
assert_eq!(
engine.request(G8lS561ApplicationManifest { id: 0, ..base }),
Err(G8lS561ApplicationLaunchFlowError::ManifestZeroId)
);
assert_eq!(engine.denial_count(), 9);
assert_eq!(engine.denial(0).unwrap().diagnostic_code, 3);
assert_eq!(engine.denial(8), None);
assert_eq!(base.validate(), Ok(G8lS561PermissionSet::from_bits(0b00_1011).unwrap()));
}
#[test]
fn name_encoding_boundaries_are_exact() {
let exact = "a".repeat(32);
let name32 = G8lS561ApplicationName::from_ascii(&exact).unwrap();
assert_eq!(name32.len(), 32);
assert_eq!(name32.as_bytes(), exact.as_bytes());
assert_eq!(name32.validate(), Ok(()));
assert_eq!(G8lS561ApplicationName::from_ascii(&"a".repeat(33)), None);
assert_eq!(G8lS561ApplicationName::from_ascii(""), None);
assert_eq!(G8lS561ApplicationName::from_ascii("tab\there"), None);
assert_eq!(G8lS561ApplicationName::from_ascii("çağrı"), None);
assert_eq!(G8lS561ApplicationName::from_ascii("del\x7f"), None);
let spaced = G8lS561ApplicationName::from_ascii("Sesli Arama ~!").unwrap();
assert_eq!(spaced.len(), 14);
assert!(!spaced.is_empty());
assert_eq!(&spaced.raw_bytes()[14..], &[0u8; 18]);
// A raw name whose declared length exceeds the buffer is clamped when
// read as bytes and rejected by validation.
let raw = G8lS561ApplicationName::from_raw([b'x'; 32], 200);
assert_eq!(raw.as_bytes().len(), 32);
assert_eq!(raw.validate(), Err(G8lS561ApplicationLaunchFlowError::ManifestNameTooLong));
}
#[test]
fn duplicate_id_fails_closed_while_resident_and_relaunch_after_exit_succeeds() {
let mut engine = G8lS561LaunchEngine::new();
let app = signed_dialer();
engine.request(app).unwrap();
for stage_command in [
G8lS561LaunchCommand::CheckPolicy(app.id),
G8lS561LaunchCommand::MintCapabilities(app.id),
G8lS561LaunchCommand::Start(app.id),
G8lS561LaunchCommand::Run(app.id),
] {
// Same id (even with a different manifest body) is rejected at every
// resident stage.
let different_body = G8lS561ApplicationManifest { version: 9, ..app };
assert_eq!(
engine.request(different_body),
Err(G8lS561ApplicationLaunchFlowError::DuplicateApplicationId)
);
engine.apply(stage_command).unwrap();
}
assert_eq!(engine.denial_count(), 4);
assert_eq!(engine.denial(0).unwrap().diagnostic_code, 11);
assert_eq!(engine.active_count(), 1);
let first_display = engine.capability(app.id, G8lS561Permission::Display).unwrap();
engine.exit(app.id, 0).unwrap();
let relaunch = engine.request(app).unwrap();
assert_eq!(relaunch.launch_index, 2);
engine.check_policy(app.id).unwrap();
engine.mint_capabilities(app.id).unwrap();
let second_display = engine.capability(app.id, G8lS561Permission::Display).unwrap();
assert_ne!(first_display.capability_id, second_display.capability_id);
assert_eq!(second_display.capability_id, s561_capability_id(2, G8lS561Permission::Display));
assert!(!second_display.revoked);
let mut state = G8lS561ApplicationLaunchFlowState::new();
let script = [G8lS561LaunchCommand::Request(app), G8lS561LaunchCommand::Request(app)];
assert_eq!(
service_s561_model_launch_flow(&mut state, &script),
Err(G8lS561ApplicationLaunchFlowError::DuplicateApplicationId)
);
assert_eq!(state.receipt(), None);
}
#[test]
fn concurrency_limit_of_eight_is_enforced() {
let mut engine = G8lS561LaunchEngine::new();
for i in 0..8u32 {
let app = manifest(0x5630 + i, G8lS561PermissionSet::DISPLAY.bits(), G8lS561TrustClass::Untrusted);
launch_to_running(&mut engine, app);
}
assert_eq!(engine.active_count(), 8);
assert_eq!(engine.peak_concurrent(), 8);
let ninth = manifest(0x5640, G8lS561PermissionSet::DISPLAY.bits(), G8lS561TrustClass::Untrusted);
assert_eq!(
engine.request(ninth),
Err(G8lS561ApplicationLaunchFlowError::ConcurrentApplicationLimit)
);
assert_eq!(engine.denial(0).unwrap().diagnostic_code, 12);
assert_eq!(engine.active_count(), 8);
assert_eq!(engine.next_launch_index(), 9);
engine.exit(0x5633, 1).unwrap();
assert_eq!(engine.active_count(), 7);
let receipt = engine.request(ninth).unwrap();
assert_eq!(receipt.launch_index, 9);
assert_eq!(engine.active_count(), 8);
assert_eq!(engine.peak_concurrent(), 8);
assert_eq!(engine.exit_record(0).unwrap().app_id, 0x5633);
assert_eq!(engine.exit_record(0).unwrap().launch_index, 4);
}
#[test]
fn exit_revokes_every_capability_and_frees_the_slot() {
let mut engine = G8lS561LaunchEngine::new();
let lab = manifest(0x5650, G8lS561PermissionSet::ALL.bits(), G8lS561TrustClass::Lab);
launch_to_running(&mut engine, lab);
let live_before: Vec<G8lS561Capability> = G8lS561Permission::ALL
.into_iter()
.map(|permission| engine.capability(lab.id, permission).unwrap())
.collect();
assert!(live_before.iter().all(|capability| !capability.revoked));
let exit = engine.exit(lab.id, 0xdead).unwrap();
assert_eq!(exit.capabilities_revoked, 6);
assert_eq!(exit.granted_permissions, 63);
assert_eq!(engine.slot(lab.id), None);
assert_eq!(engine.capability(lab.id, G8lS561Permission::Modem), None);
assert_eq!(engine.counters()[6], 6);
assert_eq!(engine.exit_count(), 1);
assert_eq!(
engine.exit_record(0),
Some(G8lS561ExitRecord { app_id: 0x5650, launch_index: 1, exit_code: 0xdead, capabilities_revoked: 6 })
);
assert_eq!(engine.exit_record(1), None);
assert_eq!(engine.exit_record(8), None);
// A second exit of the same id is an unknown application.
assert_eq!(
engine.exit(lab.id, 0),
Err(G8lS561ApplicationLaunchFlowError::UnknownApplication)
);
// Exit log wraps after eight exits.
for i in 0..8u32 {
let app = manifest(0x5660 + i, 0, G8lS561TrustClass::Untrusted);
launch_to_running(&mut engine, app);
engine.exit(app.id, i).unwrap();
}
assert_eq!(engine.exit_count(), 9);
assert_eq!(engine.exit_record(0).unwrap().app_id, 0x5667);
assert_eq!(engine.exit_record(1).unwrap().app_id, 0x5660);
}
#[test]
fn stage_order_violations_fail_closed() {
let mut engine = G8lS561LaunchEngine::new();
let app = signed_dialer();
let violation = Err(G8lS561ApplicationLaunchFlowError::StageOrderViolation);
let unknown = Err(G8lS561ApplicationLaunchFlowError::UnknownApplication);
assert_eq!(engine.check_policy(app.id), unknown);
assert_eq!(engine.exit(app.id, 0), unknown);
engine.request(app).unwrap();
assert_eq!(engine.mint_capabilities(app.id), violation);
assert_eq!(engine.start(app.id), violation);
assert_eq!(engine.run(app.id), violation);
assert_eq!(engine.exit(app.id, 0), violation);
engine.check_policy(app.id).unwrap();
assert_eq!(engine.check_policy(app.id), violation);
assert_eq!(engine.start(app.id), violation);
engine.mint_capabilities(app.id).unwrap();
assert_eq!(engine.mint_capabilities(app.id), violation);
assert_eq!(engine.run(app.id), violation);
engine.start(app.id).unwrap();
assert_eq!(engine.start(app.id), violation);
assert_eq!(engine.exit(app.id, 0), violation);
engine.run(app.id).unwrap();
assert_eq!(engine.run(app.id), violation);
assert_eq!(engine.check_policy(app.id), violation);
assert_eq!(engine.step_count(), 5);
assert_eq!(engine.denial_count(), 0);
engine.exit(app.id, 0).unwrap();
assert_eq!(engine.step_count(), 6);
let mut state = G8lS561ApplicationLaunchFlowState::new();
let script = [G8lS561LaunchCommand::Request(app), G8lS561LaunchCommand::Start(app.id)];
assert_eq!(
service_s561_model_launch_flow(&mut state, &script),
Err(G8lS561ApplicationLaunchFlowError::StageOrderViolation)
);
let script = [G8lS561LaunchCommand::Run(0x9999)];
assert_eq!(
service_s561_model_launch_flow(&mut state, &script),
Err(G8lS561ApplicationLaunchFlowError::UnknownApplication)
);
assert_eq!(state.receipt(), None);
}
#[test]
fn script_bounds_and_counter_overflow_fail_closed() {
let mut state = G8lS561ApplicationLaunchFlowState::new();
assert_eq!(
service_s561_model_launch_flow(&mut state, &[]),
Err(G8lS561ApplicationLaunchFlowError::EmptyScript)
);
let too_long = vec![G8lS561LaunchCommand::Request(signed_dialer()); 65];
assert_eq!(
service_s561_model_launch_flow(&mut state, &too_long),
Err(G8lS561ApplicationLaunchFlowError::ScriptTooLong)
);
assert_eq!(state.receipt(), None);
// Exactly 64 commands are accepted: eight full launches, then eight exits
// (8 * 5 + 8 = 48) padded with 16 more launch/exit pairs on one slot.
let mut script = Vec::new();
for i in 0..8u32 {
script.extend(s561_full_launch_commands(manifest(0x5670 + i, 0, G8lS561TrustClass::Untrusted)));
}
for i in 0..8u32 {
script.push(G8lS561LaunchCommand::Exit { app_id: 0x5670 + i, exit_code: 0 });
}
let filler = manifest(0x5680, 0, G8lS561TrustClass::Untrusted);
while script.len() < 64 {
script.push(G8lS561LaunchCommand::Request(filler));
script.push(G8lS561LaunchCommand::CheckPolicy(filler.id));
script.push(G8lS561LaunchCommand::MintCapabilities(filler.id));
script.push(G8lS561LaunchCommand::Start(filler.id));
script.push(G8lS561LaunchCommand::Run(filler.id));
script.push(G8lS561LaunchCommand::Exit { app_id: filler.id, exit_code: 0 });
if script.len() > 64 {
script.truncate(64);
}
}
assert_eq!(script.len(), 64);
let G8lS561ApplicationLaunchFlowOutcome::Published(receipt) =
service_s561_model_launch_flow(&mut state, &script).unwrap()
else {
panic!("64-command script must publish")
};
assert_eq!(receipt.command_count, 64);
assert_eq!(receipt.step_count, 64);
assert_eq!(receipt.peak_concurrent_applications, 8);
let mut engine = G8lS561LaunchEngine::with_next_launch_index(u32::MAX - 1);
let last = engine.request(manifest(0x5690, 0, G8lS561TrustClass::Lab)).unwrap();
assert_eq!(last.launch_index, u32::MAX - 1);
assert_eq!(engine.next_launch_index(), u32::MAX);
// The launch that would exhaust the index space fails closed and
// mutates nothing.
assert_eq!(
engine.request(manifest(0x5691, 0, G8lS561TrustClass::Lab)),
Err(G8lS561ApplicationLaunchFlowError::CounterOverflow)
);
assert_eq!(engine.active_count(), 1);
assert_eq!(engine.next_launch_index(), u32::MAX);
}
#[test]
fn fnv1a_64_matches_reference_vectors_and_step_receipt_encoding() {
assert_eq!(s561_fnv1a_64(S561_FNV1A_OFFSET_BASIS, b""), 0xcbf2_9ce4_8422_2325);
assert_eq!(s561_fnv1a_64(S561_FNV1A_OFFSET_BASIS, b"a"), 0xaf63_dc4c_8601_ec8c);
assert_eq!(s561_fnv1a_64(S561_FNV1A_OFFSET_BASIS, b"foobar"), 0x8594_4171_f739_67e8);
let receipt = G8lS561LaunchStepReceipt {
app_id: 0x0403_0201,
launch_index: 0x0807_0605,
previous_stage_code: 5,
stage: G8lS561LaunchStage::Exited,
granted_permissions: 0b11_1111,
capabilities_minted: 0,
capabilities_revoked: 6,
exit_code: 0x0d0c_0b0a,
};
assert_eq!(
receipt.encode(),
[1, 2, 3, 4, 5, 6, 7, 8, 5, 6, 63, 0, 6, 0x0a, 0x0b, 0x0c, 0x0d]
);
assert_eq!(S561_STEP_RECEIPT_BYTES, 17);
}
#[test]
fn reference_script_pins_exact_trace_checksum_and_counts() {
let mut state = G8lS561ApplicationLaunchFlowState::new();
let G8lS561ApplicationLaunchFlowOutcome::Published(receipt) =
service_s561_model_launch_flow(&mut state, &reference_script()).unwrap()
else {
panic!("reference script must publish")
};
assert_eq!(receipt.command_count, 11);
assert_eq!(receipt.step_count, 11);
assert_eq!(receipt.launches_requested, 2);
assert_eq!(receipt.policy_checks_passed, 2);
assert_eq!(receipt.capabilities_minted, 4);
assert_eq!(receipt.applications_started, 2);
assert_eq!(receipt.applications_run, 2);
assert_eq!(receipt.applications_exited, 1);
assert_eq!(receipt.capabilities_revoked, 1);
assert_eq!(receipt.denials_recorded, 0);
assert_eq!(receipt.peak_concurrent_applications, 2);
assert_eq!(receipt.final_resident_applications, 1);
assert_eq!(receipt.trace_checksum, REFERENCE_SCRIPT_TRACE_CHECKSUM);
// Independent re-fold of the same eleven step receipts.
let mut engine = G8lS561LaunchEngine::new();
let mut folded = S561_FNV1A_OFFSET_BASIS;
for command in reference_script() {
let step = engine.apply(command).unwrap();
folded = s561_fnv1a_64(folded, &step.encode());
}
assert_eq!(folded, REFERENCE_SCRIPT_TRACE_CHECKSUM);
assert_eq!(engine.trace_checksum(), REFERENCE_SCRIPT_TRACE_CHECKSUM);
assert_eq!(state.engine().trace_checksum(), REFERENCE_SCRIPT_TRACE_CHECKSUM);
assert_eq!(state.engine().slot(0x5601).unwrap().stage, G8lS561LaunchStage::Running);
assert_eq!(state.engine().slot(0x5602), None);
}
#[test]
fn receipt_carries_scope_limits_and_zero_claims() {
let mut state = G8lS561ApplicationLaunchFlowState::default();
let G8lS561ApplicationLaunchFlowOutcome::Published(receipt) =
service_s561_model_launch_flow(&mut state, &reference_script()).unwrap()
else {
panic!("reference script must publish")
};
assert_eq!(receipt.sequence, 561);
assert_eq!(receipt.predecessor_sequence, 560);
assert_eq!(receipt.r1_stage, 4);
assert_eq!(receipt.max_concurrent_applications, 8);
assert!(receipt.s540_physical_verdict_retained_red);
assert!(receipt.s543_physical_verdict_retained_red);
assert!(!receipt.automatic_promotion);
assert!(!receipt.hardware_present);
assert_eq!(receipt.supported_profile_runtime_observations, 0);
assert_eq!(receipt.physical_observations, 0);
assert!(!receipt.runbook_executed);
let fresh = G8lS561LaunchEngine::default();
assert_eq!(fresh, G8lS561LaunchEngine::new());
assert_eq!(fresh.trace_checksum(), S561_FNV1A_OFFSET_BASIS);
assert_eq!(fresh.next_launch_index(), 1);
}
#[test]
fn source_only_gate_keeps_runtime_physical_and_r1_claims_zero() {
assert!(SOURCE.contains("S561_SUPPORTED_PROFILE_RUNTIME_OBSERVATIONS: usize = 0"));
assert!(SOURCE.contains("S561_PHYSICAL_OBSERVATIONS: usize = 0"));
assert!(SOURCE.contains("S561_PHYSICAL_OR_DEVICE_OPERATIONS: usize = 0"));
assert!(SOURCE.contains("S561_HARDWARE_PRESENT: bool = false"));
assert!(SOURCE.contains("S561_BOOT_TO_UI_PHYSICALLY_OBSERVED: bool = false"));
assert!(SOURCE.contains("S561_R1_ACCEPTANCE_COMPLETE: bool = false"));
assert!(SOURCE.contains("RUNBOOK_EXECUTED_IN_S561: bool = false"));
assert!(SOURCE.contains("S561_MAX_CONCURRENT_APPLICATIONS: usize = 8"));
}
snippet sha256: 1636d7fee865…file sha256: 1636d7fee865…
03 · Kapı kimlik kaydı
Operations sıra, kimlik ve başlık bağı
tam Operations kaydıL2245–L2305
website/src/lib/operations.ts::g8l-s561-r1-permissioned-application-launch-flow-model
{
id: "g8l-s561-r1-permissioned-application-launch-flow-model",
date: "2026-08-30",
sequence: 561,
status: "passed",
umbrella_status: "partial",
title: "S561 · R1 uygulama: izinli uygulama başlatma akışı modeli",
summary:
"S561 kaynak/host model kapısı PASS'tir: uygulama manifest'i (nonzero id, ≤32 bayt yazdırılabilir ASCII ad, paketli sürüm, {DISPLAY, INPUT, MODEM, AUDIO, STORAGE, NETWORK} izin bitset'i, 32 bayt ELF hash, güven sınıfı), güven sınıfı başına izin veren tablo güdümlü politika (Lab=hepsi, Signed=MODEM hariç hepsi, Untrusted=yalnız DISPLAY+INPUT) ve Requested→PolicyChecked→CapabilitiesMinted→Started→Running→Exited başlatma hattı receipt'lerle modellenmiştir. Capability'ler yalnız verilen izinler için deterministik id'lerle mint edilir, çıkışta tamamı revoke edilir ve slot serbest bırakılır; aynı anda en fazla 8 uygulama residenttir ve dokuzuncu istek reddedilir. Manifest hatası, izin escalation'ı, duplicate id, sıra dışı aşama geçişi ve 64 komut üstü script fail-closed'dur; referans script trace checksum'ı 0xc64036438d60dd56 olarak sabitlenmiştir. Focused 22/22 PASS'tir; hiçbir ELF loader, scheduler task, IPC endpoint veya production çağrı noktası yoktur. S540 ve S543 fiziksel raw/verdict değişmez RED kalır; physical observation=0, RUNBOOK_EXECUTED_IN_S561=NO, Boot-to-UI=false ve R1 acceptance=false'dur. S562, S561 başlatma hattı şekilleri üzerine kurulan host-only servis kill/restart süpervizyon modeli kapısıdır.",
evidence: [
"S561, S560'tan ayrı kaynak modülü, 22-test focused binary, proof, status manifest, Operations kaydı ve complete Code kartına sahiptir; production callsite eklenmemiştir.",
"Dar S561 source/host status=PASS; R1 umbrella=PARTIAL, R1 aşaması 4 (uygulama, recovery ve update gösterimi); S540 ve S543 physical gate status=RED olarak ayrı tutulur.",
"Manifest doğrulaması fail-closed'dur: sıfır id, boş/32 bayt üstü/yazdırılamaz veya ASCII dışı/sıfır dolgusuz ad, sıfır sürüm, bilinmeyen izin biti ve tamamı sıfır ELF hash ManifestZeroId..ManifestZeroElfHash kodlarıyla reddedilir ve bounded denial log'una işlenir.",
"İzin bitset'i exact'tır: DISPLAY=1, INPUT=2, MODEM=4, AUDIO=8, STORAGE=16, NETWORK=32; tüm bitler 0b111111'dir ve 63 üstü bit ManifestUnknownPermissionBits verir.",
"Politika tablosu exact'tır: Lab 0b111111, Signed 0b111011 (MODEM asla verilmez), Untrusted 0b000011; istenen küme izin kümesinin alt kümesi değilse PermissionEscalation ile slot serbest bırakılır ve requested/allowed/denied bitleri denial kaydına yazılır.",
"Başlatma hattı exact sıra Requested(1)→PolicyChecked(2)→CapabilitiesMinted(3)→Started(4)→Running(5)→Exited(6) biçimindedir; her geçiş 17 baytlık kodlanmış G8lS561LaunchStepReceipt döndürür ve FNV-1a 64 trace checksum'ına katlanır; sıra dışı her geçiş StageOrderViolation, bilinmeyen id UnknownApplication verir.",
"Capability mint yalnız verilen izinler içindir: capability id deterministik (launch_index << 8) | permission_bit'tir; Signed dialer örneği 3, Lab tam-izin örneği 6, sıfır-izin örneği 0 capability üretir.",
"Çıkışta canlı capability'lerin tamamı revoke edilir, slot serbest kalır ve bounded exit kaydı (8'lik ring) app_id/launch_index/exit_code/revoked sayısını tutar; aynı id yeni launch_index ve yeni capability id'leriyle yeniden başlatılabilir.",
"Eşzamanlılık sınırı 8'dir: dokuzuncu istek ConcurrentApplicationLimit ile reddedilir ve denial log'una yazılır; bir uygulama çıktıktan sonra aynı istek kabul edilir; peak concurrency receipt'te taşınır.",
"Duplicate id denetimi resident her aşamada uygulanır: Requested..Running arasındaki bir id yeniden istenirse DuplicateApplicationId döner; farklı manifest gövdesi bunu aşamaz.",
"service_s561_model_launch_flow 1..=64 komutluk script'i taze engine üzerinde koşar; boş script EmptyScript, 65 komut ScriptTooLong verir; exact tekrar aynı receipt ile Retained döner ve yayın sonrası farklı exit kodu, eksik komut veya yeniden sıralanmış script PublishedStateDrift ile reddedilir.",
"Referans script (iki tam başlatma + bir çıkış, 11 adım) exact sayaçları ve trace checksum 0xc64036438d60dd56 değerini sabitler; FNV-1a 64 referans vektörleri (boş, 'a', 'foobar') ayrıca doğrulanır.",
"On yedi hata kodu sıfırdan farklı ve benzersizdir; launch index ve tüm sayaçlar checked aritmetikle taşmaya karşı CounterOverflow ile korunur.",
"Focused target 1 grup / 22 passed / 0 failed / 0 ignored / 0 filtered verdi.",
"Implementation 37668 B / cc5be8c3b0fb0e44b9de71ea3da635f0218b7ecd57ba9d0ab9a58e01c0b9de22; focused test 32223 B / 1636d7fee86573244082da7be988a2d602d9af5c0df490dea896246ce36d24e7 SHA-256'dır.",
"Proof 5056 B'dir.",
"S540 immutable raw 20525 B ve S543 immutable raw 20509 B byte-exact korunur; her ikisi RED, automatic promotion=false ve rerun=false'dur.",
"S561 sırasında ELF loader, scheduler task, IPC endpoint, gerçek capability tablosu, SD write/read-back/eject, UART open/capture, power transition veya yeni immutable raw üretimi yapılmadı.",
"RUNBOOK_EXECUTED_IN_S561=NO; supported-profile runtime observations=0, physical observations=0, hardware present=false, Boot-to-UI physically observed=false ve R1 acceptance=false'dur.",
"S562 yalnız host üzerinde S561 başlatma hattı şekilleri üzerine servis kill/restart süpervizyon modelini kuracaktır; aygıt veya fiziksel koşu yetkisi değildir.",
],
commands: [
"CARGO_INCREMENTAL=0 cargo test -p aselsan_microkernel_simulation --test g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s561_r1_permissioned_application_launch_flow_model -- --test-threads=1",
],
terminalSessions: [
{
id: "s561-focused",
title: "S561 izinli uygulama başlatma akışı focused kabulü",
commandLines: [
"CARGO_INCREMENTAL=0 cargo test -p aselsan_microkernel_simulation --test g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s561_r1_permissioned_application_launch_flow_model -- --test-threads=1",
],
outputLines: [
"test result: ok. 22 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s",
"S561 focused=1 group / 22 passed / 0 failed",
"hardware=none physical=0 runbook=NO",
],
exitCode: 0,
outputMode: "complete",
},
],
terminalSessionsNote:
"S561 kaynak/host model PASS'tir; supported-profile runtime veya fiziksel PASS değildir. S540 ve S543 RED raw ve kararları değişmez.",
limitations: [
"S561 yalnız host üzerinde derlenen ve focused testle sürülen bir modeldir; hiçbir donanım/panel/modem/board gözlemi yoktur.",
"Model hiçbir gerçek ELF yüklemez, task başlatmaz, IPC endpoint açmaz veya gerçek capability tablosuna dokunmaz; başlatma hattı yalnız receipt ve checksum assertion'ları ile doğrulanır.",
"Modül hiçbir boot, IRQ, scheduler veya driver yoluna bağlanmamıştır; production callsite wired=false'dur.",
"S540 ve S543 fiziksel RED immutable kalır; S546 üçüncü fiziksel koşu ayrı bir kapıdır ve kararı burada varsayılmaz.",
"Boot-to-UI fiziksel olarak gözlenmedi; Boot-to-UI ve R1 acceptance false kalır.",
"S562 host-only servis kill/restart süpervizyon modeli tamamlanmadan uygulama yaşam döngüsü yolu için yeni bir fiziksel aday yoktur.",
],
},snippet sha256: 2665c83f896e…file sha256: 9726dbf00f84…
Focused test komutu
CARGO_INCREMENTAL=0 cargo test -p aselsan_microkernel_simulation --test g8l_target_dispatch_scheduler_owner_scheduler_mutation_production_migration_lifecycle_s561_r1_permissioned_application_launch_flow_model -- --test-threads=1proof: docs/M8.1-RPi5-G8l-S561-R1-Permissioned-Application-Launch-Flow-Model-Proof.md
Registry schema v5 · generator
website/scripts/generate-code-gates.mjs · Tam SHA-256: 91d38c7b6222f0b4c117be786454853543da55a160e543d9b951057cc20dcc06