Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion appleApp/FTWKit/Sources/FTWKit/Protocol/Contract.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public enum Contract {
"ftw.plan.read",
"ftw.health.read",
"ftw.assets.read",
"ftw.events.read",
"ftw.dispatch.write",
"ftw.mode.write",
"ftw.members.read",
Expand Down Expand Up @@ -43,6 +44,7 @@ public enum Contract {
"cmd.lease", "cmd.precondition", "cmd.readback",
"der.battery", "der.ev", "der.v2x",
"plan.dispatch", "net.webrtc", "price.spot", "api.passthrough",
"cloud.link",
]

public static let capPlanDispatch = "plan.dispatch"
Expand Down Expand Up @@ -119,5 +121,5 @@ public enum Contract {
}

public static let sourceStates = ["live", "lagging", "stale", "down", "never"]
public static let carrierStates = ["webrtc", "relay", "cache", "none"]
public static let carrierStates = ["webrtc", "relay", "cloud", "cache", "none"]
}
3 changes: 2 additions & 1 deletion appleApp/FTWKit/Sources/FTWShared/Readings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import Foundation
/// with it: "connected, but the inverter went quiet 40 seconds ago" needs
/// both.
public enum CarrierKind: String, Sendable, Codable {
case webrtc, relay, cache, none
/// `cloud` is data read through FTW Cloud. Nothing produces it yet.
case webrtc, relay, cloud, cache, none
}

public enum SourceState: String, Sendable, Codable, Comparable {
Expand Down
2 changes: 1 addition & 1 deletion appleApp/FTWKit/Tests/FTWKitTests/ContractTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import FTWDemo
/// FTWShared spells the same states as enums, for widgets that never
/// link FTWKit.
@Test func sharedStatesAreTheRegistrys() {
#expect([CarrierKind.webrtc, .relay, .cache, .none].map(\.rawValue) == Contract.carrierStates)
#expect([CarrierKind.webrtc, .relay, .cloud, .cache, .none].map(\.rawValue) == Contract.carrierStates)
#expect([SourceState.live, .lagging, .stale, .down, .never].map(\.rawValue) == Contract.sourceStates)
#expect(Contract.sourceStates.allSatisfy { SourceState(rawValue: $0) != nil })
#expect(Contract.carrierStates.allSatisfy { CarrierKind(rawValue: $0) != nil })
Expand Down
27 changes: 24 additions & 3 deletions protocol/registry.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
# The single source for every name shared between this app and the box, and
# the same file in both repositories — byte for byte.
#
# In srcfl/ftw it generates Go constants: go/internal/appproto/contract_gen.go
# and go/internal/apiauth/contract_gen.go, from `go generate ./internal/...`.
# In srcfl/ftw it generates Go constants: go/protocol/contract_gen.go and
# go/internal/apiauth/contract_gen.go, from `go generate ./internal/...`.
# In srcfl/ftw-webapp there is no generator — src/lib/protocol/contract.ts and
# the error table in src/lib/protocol/messages.ts are written by hand and read
# back against this file by tests/registry-contract.test.ts.
Expand Down Expand Up @@ -76,6 +76,10 @@ capabilities:
# history.5m. Present does not mean every path is reachable: reads and
# configuration go through, anything that moves energy stays on cmd.
- api.passthrough
# The box can run the FTW Cloud uplink. Absent means the app uses the relay
# only and offers no FTW Cloud. It says nothing about whether the owner
# turned FTW Cloud on; the box's grant decides that.
- cloud.link

# ---------------------------------------------------------------------------
# Scopes. One object axis, two verb axes: <object>.<read|write>.
Expand All @@ -87,6 +91,7 @@ scopes:
- { name: ftw.plan.read, desc: Dispatch plan and its reasoning }
- { name: ftw.health.read, desc: Box and driver health }
- { name: ftw.assets.read, desc: Devices and their capabilities }
- { name: ftw.events.read, desc: Events behind notifications }
- { name: ftw.dispatch.write, desc: Control energy assets }
- { name: ftw.mode.write, desc: Change site operating mode }
- { name: ftw.members.read, desc: See who has access }
Expand All @@ -100,6 +105,22 @@ roles:
label: Viewer
scopes: [ftw.live.read]

# ---------------------------------------------------------------------------
# FTW Cloud. What an owner can let the box share with FTW Cloud, and why.
# docs/protocol-cloud.md in srcfl/ftw is the wire format.
#
# cloud_scopes are the scopes a grant may name: the box sends a class only
# when the grant holds its scope, and a device reads it only when its own
# role carries the scope too. ftw.assets.read is left out on purpose: the
# box sends no device inventory, names or capabilities to FTW Cloud.
#
# cloud_purposes are what the data may be used for. serve is the owner's own
# phones and widgets. stats is household statistics, a separate consent that
# is off unless the owner turns it on.
# ---------------------------------------------------------------------------
cloud_scopes: [ftw.live.read, ftw.history.read, ftw.plan.read, ftw.health.read, ftw.events.read]
cloud_purposes: [serve, stats]

# ---------------------------------------------------------------------------
# Command operations. What a `cmd` frame may name, and the scope the grant
# must carry before the box acts on it.
Expand Down Expand Up @@ -219,7 +240,7 @@ client_errors:
# the two into one enum cannot express "connected, but the inverter is quiet".
# ---------------------------------------------------------------------------
source_states: [live, lagging, stale, down, never]
carrier_states: [webrtc, relay, cache, none]
carrier_states: [webrtc, relay, cloud, cache, none]

# ---------------------------------------------------------------------------
# History resolutions backed by what the box actually stores.
Expand Down
Loading