Skip to content
Open
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
38 changes: 25 additions & 13 deletions src/cli/broker.toit
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import .config
import .device
import .pod
import .pod-specification
import .scope show Scope

import .utils
import .utils.patch-build show build-diff-patch build-trivial-patch
Expand Down Expand Up @@ -103,6 +104,16 @@ class Broker:
cli_.ui.abort "$error-message (broker)."
return broker-connection__

/**
The $Scope to use when talking to the broker.

For now derived directly from $organization-id. When the fleet file gains
a per-service scope field this will return the broker's own configured
scope instead.
*/
scope -> Scope:
return Scope.from-organization-id organization-id

short-string-for_ --device-id/Uuid -> string:
if not device-short-strings_: throw "Access to device in non-device fleet."
return device-short-strings_[device-id]
Expand Down Expand Up @@ -147,7 +158,7 @@ class Broker:
--part-id=id
cli_.cache.get-file-path key: | store/FileStore |
broker-connection_.pod-registry-upload-pod-part contents --part-id=id
--organization-id=organization-id
--scope=scope
store.save contents
key := cache-key-pod-manifest
--broker-config=server-config
Expand All @@ -156,12 +167,12 @@ class Broker:
cli_.cache.get-file-path key: | store/FileStore |
encoded := ubjson.encode manifest
broker-connection_.pod-registry-upload-pod-manifest encoded --pod-id=pod.id
--organization-id=organization-id
--scope=scope
store.save encoded

description-ids := broker-connection_.pod-registry-descriptions
--fleet-id=fleet-id
--organization-id=organization-id
--scope=scope
--names=[pod.name]
--create-if-absent

Expand Down Expand Up @@ -222,7 +233,7 @@ class Broker:
cli_.cache.get cache-key: | store/FileStore |
trivial := build-trivial-patch patch.bits_
broker-connection_.upload-firmware trivial
--organization-id=organization-id
--scope=scope
--firmware-id=trivial-id
store.save-via-writer: | writer/io.Writer |
trivial.do: writer.write it
Expand All @@ -239,7 +250,7 @@ class Broker:
trivial-old := cli_.cache.get cache-key: | store/FileStore |
downloaded := null
catch: downloaded = broker-connection_.download-firmware
--organization-id=organization-id
--scope=scope
--id=old-id
if not downloaded:
cli_.ui.emit --warning "Failed to download old firmware for patch $old-id -> $trivial-id."
Expand Down Expand Up @@ -278,7 +289,7 @@ class Broker:
to64 := base64.encode patch.to_ --url-mode
cli_.ui.emit --info "Uploading patch $from64 -> $to64 ($diff-size)."
broker-connection_.upload-firmware diff
--organization-id=organization-id
--scope=scope
--firmware-id=diff-id
store.save-via-writer: | writer/io.Writer |
diff.do: writer.write it
Expand Down Expand Up @@ -313,7 +324,7 @@ class Broker:
encoded-manifest := cli_.cache.get manifest-key: | store/FileStore |
bytes := broker-connection_.pod-registry-download-pod-manifest
--pod-id=pod-id
--organization-id=organization-id
--scope=scope
store.save bytes
manifest := ubjson.decode encoded-manifest
return Pod.from-manifest
Expand All @@ -327,7 +338,7 @@ class Broker:
cli_.cache.get key: | store/FileStore |
bytes := broker-connection_.pod-registry-download-pod-part
part-id
--organization-id=organization-id
--scope=scope
store.save bytes

list-pods --names/List -> Map:
Expand All @@ -337,7 +348,7 @@ class Broker:
else:
descriptions = broker-connection_.pod-registry-descriptions
--fleet-id=fleet-id
--organization-id=organization-id
--scope=scope
--names=names
--no-create-if-absent
result := {:}
Expand All @@ -349,7 +360,7 @@ class Broker:
delete --description-names/List:
descriptions := broker-connection_.pod-registry-descriptions
--fleet-id=fleet-id
--organization-id=organization-id
--scope=scope
--names=description-names
--no-create-if-absent
unknown-pod-descriptions := []
Expand Down Expand Up @@ -418,7 +429,7 @@ class Broker:

descriptions := broker-connection_.pod-registry-descriptions
--fleet-id=fleet-id
--organization-id=organization-id
--scope=scope
--names=names.to-list
--no-create-if-absent

Expand Down Expand Up @@ -762,13 +773,14 @@ class Broker:
store.with-tmp-directory: | tmp-dir |
// TODO(florian): do we want to rely on the cache, or should we
// do a check to see if the files are really uploaded?
device-scope := Scope.from-organization-id device.organization-id
broker-connection_.upload-image program.image32
--app-id=id
--organization-id=device.organization-id
--scope=device-scope
--word-size=32
file.write-contents program.image32 --path="$tmp-dir/image32.bin"
broker-connection_.upload-image program.image64
--organization-id=device.organization-id
--scope=device-scope
--app-id=id
--word-size=64
file.write-contents program.image64 --path="$tmp-dir/image64.bin"
Expand Down
25 changes: 13 additions & 12 deletions src/cli/brokers/broker.toit
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ..config
import ..event
import ..device
import ..pod-registry
import ..scope show Scope
import ...shared.server-config
import .supabase
import .http.base
Expand Down Expand Up @@ -98,29 +99,29 @@ interface BrokerCli implements Authenticatable:

/**
Uploads an application image with the given $app-id so that a device in
$organization-id can fetch it.
$scope can fetch it.

There may be multiple images for the same $app-id, that differ in the $word-size.
Generally $word-size is either 32 or 64.
*/
upload-image
--organization-id/Uuid
--scope/Scope
--app-id/Uuid
--word-size/int
contents/ByteArray -> none

/**
Uploads a firmware with the given $firmware-id so that a device in
$organization-id can fetch it.
$scope can fetch it.

The $chunks are a list of byte arrays.
*/
upload-firmware --organization-id/Uuid --firmware-id/string chunks/List -> none
upload-firmware --scope/Scope --firmware-id/string chunks/List -> none

/**
Downloads a firmware chunk inside the given $organization-id.
Downloads a firmware chunk inside the given $scope.
*/
download-firmware --organization-id/Uuid --id/string -> ByteArray
download-firmware --scope/Scope --id/string -> ByteArray

/**
Informs the broker that a device with the given $device-id has been provisioned.
Expand Down Expand Up @@ -154,7 +155,7 @@ interface BrokerCli implements Authenticatable:
*/
pod-registry-description-upsert -> int
--fleet-id/Uuid
--organization-id/Uuid
--scope/Scope
--name/string
--description/string?

Expand Down Expand Up @@ -217,7 +218,7 @@ interface BrokerCli implements Authenticatable:
*/
pod-registry-descriptions -> List
--fleet-id/Uuid
--organization-id/Uuid
--scope/Scope
--names/List
--create-if-absent/bool

Expand Down Expand Up @@ -249,14 +250,14 @@ interface BrokerCli implements Authenticatable:
Uploads a pod part to the registry.
*/
pod-registry-upload-pod-part -> none
--organization-id/Uuid
--scope/Scope
--part-id/string
contents/ByteArray

/**
Downloads a pod part from the registry.
*/
pod-registry-download-pod-part part-id/string --organization-id/Uuid -> ByteArray
pod-registry-download-pod-part part-id/string --scope/Scope -> ByteArray

/**
Saves the manifest of a pod.
Expand All @@ -265,15 +266,15 @@ interface BrokerCli implements Authenticatable:
a pod from its parts.
*/
pod-registry-upload-pod-manifest -> none
--organization-id/Uuid
--scope/Scope
--pod-id/Uuid
contents/ByteArray

/**
Downloads the manifest of a pod.
*/
pod-registry-download-pod-manifest -> ByteArray
--organization-id/Uuid
--scope/Scope
--pod-id/Uuid

with-broker server-config/ServerConfig --cli/Cli [block]:
Expand Down
30 changes: 20 additions & 10 deletions src/cli/brokers/http/base.toit
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import ..broker
import ...device
import ...event
import ...pod-registry
import ...scope show Scope
import ....shared.server-config
import ....shared.utils as utils
import ....shared.constants show *
Expand Down Expand Up @@ -183,24 +184,27 @@ class BrokerCliHttp implements BrokerCli:
return result

upload-image -> none
--organization-id/Uuid
--scope/Scope
--app-id/Uuid
--word-size/int
contents/ByteArray:
organization-id := scope.as-uuid
send-request_ COMMAND-UPLOAD_ {
"path": "/toit-artemis-assets/$organization-id/images/$app-id.$word-size",
"content": contents,
}

upload-firmware --organization-id/Uuid --firmware-id/string chunks/List -> none:
upload-firmware --scope/Scope --firmware-id/string chunks/List -> none:
organization-id := scope.as-uuid
firmware := #[]
chunks.do: firmware += it
send-request_ COMMAND-UPLOAD_ {
"path": "/toit-artemis-assets/$organization-id/firmware/$firmware-id",
"content": firmware,
}

download-firmware --organization-id/Uuid --id/string -> ByteArray:
download-firmware --scope/Scope --id/string -> ByteArray:
organization-id := scope.as-uuid
return send-request_ COMMAND-DOWNLOAD_ {
"path": "/toit-artemis-assets/$organization-id/firmware/$id",
}
Expand Down Expand Up @@ -241,9 +245,10 @@ class BrokerCliHttp implements BrokerCli:
/** See $BrokerCli.pod-registry-description-upsert. */
pod-registry-description-upsert -> int
--fleet-id/Uuid
--organization-id/Uuid
--scope/Scope
--name/string
--description/string?:
organization-id := scope.as-uuid
return send-request_ COMMAND-POD-REGISTRY-DESCRIPTION-UPSERT_ {
"_fleet_id": "$fleet-id",
"_organization_id": "$organization-id",
Expand Down Expand Up @@ -310,12 +315,13 @@ class BrokerCliHttp implements BrokerCli:
}
return response.map: PodRegistryDescription.from-map it

/** See $(BrokerCli.pod-registry-descriptions --fleet-id --organization-id --names --create-if-absent). */
/** See $(BrokerCli.pod-registry-descriptions --fleet-id --scope --names --create-if-absent). */
pod-registry-descriptions -> List
--fleet-id/Uuid
--organization-id/Uuid
--scope/Scope
--names/List
--create-if-absent/bool:
organization-id := scope.as-uuid
response := send-request_ COMMAND-POD-REGISTRY-DESCRIPTIONS-BY-NAMES_ {
"_fleet_id": "$fleet-id",
"_organization_id": "$organization-id",
Expand Down Expand Up @@ -365,32 +371,36 @@ class BrokerCliHttp implements BrokerCli:

/** See $BrokerCli.pod-registry-upload-pod-part. */
pod-registry-upload-pod-part -> none
--organization-id/Uuid
--scope/Scope
--part-id/string
contents/ByteArray:
organization-id := scope.as-uuid
send-request_ COMMAND-UPLOAD_ {
"path": "/toit-artemis-pods/$organization-id/part/$part-id",
"content": contents,
}

/** See $BrokerCli.pod-registry-download-pod-part. */
pod-registry-download-pod-part part-id/string --organization-id/Uuid -> ByteArray:
pod-registry-download-pod-part part-id/string --scope/Scope -> ByteArray:
organization-id := scope.as-uuid
return send-request_ COMMAND-DOWNLOAD-PRIVATE_ {
"path": "/toit-artemis-pods/$organization-id/part/$part-id",
}

/** See $BrokerCli.pod-registry-upload-pod-manifest. */
pod-registry-upload-pod-manifest -> none
--organization-id/Uuid
--scope/Scope
--pod-id/Uuid
contents/ByteArray:
organization-id := scope.as-uuid
send-request_ COMMAND-UPLOAD_ {
"path": "/toit-artemis-pods/$organization-id/manifest/$pod-id",
"content": contents,
}

/** See $BrokerCli.pod-registry-download-pod-manifest. */
pod-registry-download-pod-manifest --organization-id/Uuid --pod-id/Uuid -> ByteArray:
pod-registry-download-pod-manifest --scope/Scope --pod-id/Uuid -> ByteArray:
organization-id := scope.as-uuid
return send-request_ COMMAND-DOWNLOAD-PRIVATE_ {
"path": "/toit-artemis-pods/$organization-id/manifest/$pod-id",
}
42 changes: 42 additions & 0 deletions src/cli/scope.toit
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (C) 2026 Toit contributors.

import uuid show Uuid

/**
A per-service authentication scope.

A $Scope is the additional bit of information a service needs, on top of the
user's session, to know which slice of resources the operation applies to.
The user's identity comes from their auth provider session (stored in the
CLI's config); the scope comes from the fleet file.

For now a $Scope always wraps an organization-id UUID. In the future scopes
will become opaque, JSON-encodable blobs that each service's auth provider
interprets independently. Calling code that needs a UUID today should go
through $as-uuid so the conversion point is greppable when the underlying
representation broadens.
*/
class Scope:
organization-id_/Uuid

constructor.from-organization-id organization-id/Uuid:
organization-id_ = organization-id

/**
Returns the scope as a UUID.

Today the scope is always a UUID; the throw is here for the future
when scopes can also be other shapes.
*/
as-uuid -> Uuid:
return organization-id_

operator == other -> bool:
if other is not Scope: return false
return organization-id_ == (other as Scope).organization-id_

hash-code -> int:
return organization-id_.hash-code

stringify -> string:
return "Scope($organization-id_)"
Loading
Loading