Skip to content

feat: custom cloud mappers#4009

Merged
jarhodes314 merged 35 commits intothin-edge:mainfrom
jarhodes314:proposal/generic-mapper
Mar 26, 2026
Merged

feat: custom cloud mappers#4009
jarhodes314 merged 35 commits intothin-edge:mainfrom
jarhodes314:proposal/generic-mapper

Conversation

@jarhodes314
Copy link
Copy Markdown
Contributor

@jarhodes314 jarhodes314 commented Feb 24, 2026

Proposed changes

In addition to the openspec proposal, this PR adds a custom cloud mapper that can be run using tedge-mapper custom (the idea being that the user names their custom cloud implementation and then runs this like any other mapper).

The mapper reads its config from a mapper config directory (e.g. /etc/tedge/mappers/thingsboard for tedge-mapper thingsboard). You can (optionally) connect a built-in bridge by adding a mapper.toml file:

url = "127.0.0.1:8883"

You can then add bridge config templates to the bridge/ subdirectory and flows to the flows/ subdirectory.

Still TODO

  • Ability to disable TLS for the bridge connection (i.e. connect to insecure cloud brokers)
  • Handle profiles somewhat gracefully in tedge mapper/tedge bridge subcommands (currently they are totally unsupported)
  • Basic tedge connect --test support
  • Configurable bridge rules for aws/az mappers
  • Improve relative path support - tedge mapper config get will currently display the naively generated path including any ..
  • Consider renaming ${config.} to ${tedge.} in bridge templates
  • Disable mapper.toml without bridge directory warning for built-in mappers

Proposal

A proposal for how to implement a generic mapper using Openspec. In short, openspec is an IMO very useful tool for making design decisions with AI agents that can later serve as a source of truth.

The first commit is a bunch of files generated by openspec to make it actually work. It's probably worth some discussion about whether we think it's a useful tool.

The (at the time of writing, only) other commit is the actual proposal and design docs for implementing a generic mapper that can tie together the flows and configurable bridge. I would suggest reading it in the order proposal.md, design.md then the two spec.mds.

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Improvement (general improvements like code refactoring that doesn't explicitly fix a bug or add any new functionality)
  • Documentation Update (if none of the other choices apply)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Paste Link to the issue


Checklist

  • I have read the CONTRIBUTING doc
  • I have signed the CLA (in all commits with git commit -s. You can activate automatic signing by running just prepare-dev once)
  • I ran just format as mentioned in CODING_GUIDELINES
  • I used just check as mentioned in CODING_GUIDELINES
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Further comments

Comment thread openspec/changes/generic-mapper/proposal.md Outdated
Comment thread openspec/changes/generic-mapper/proposal.md Outdated

Custom mapper config is isolated — only the custom mapper itself uses it, unlike the global `tedge_config` schema which is referenced throughout thin-edge. This suggests custom mapper settings should live in the mapper's own `tedge.toml` (e.g. `/etc/tedge/mappers/_thingsboard/tedge.toml`), separate from the `define_tedge_config!` macro. Users edit this file directly rather than using `tedge config set/get`.

This avoids the complexity of fitting dynamic/unknown mapper names into the compile-time config macro, and is acceptable because the users writing custom mappers are technical enough to edit TOML files. The bridge template system would need a new `${mapper.*}` variable namespace to let bridge rule templates reference the mapper's own config (analogous to how `${config.c8y.url}` references global config today). `tedge config` support could potentially be added later if demand warrants it (though this might prove complex to implement in practice).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, the primary use-case for tedge config over editing these toml files by hand was to enable easier iterative updates to the config from automation scripts. So, I wouldn't rule out its utility.

Comment thread openspec/changes/archive/2026-03-18-generic-mapper/proposal.md Outdated
Comment thread openspec/changes/generic-mapper/design.md Outdated
Comment thread openspec/changes/generic-mapper/design.md Outdated
Comment thread openspec/changes/generic-mapper/design.md Outdated
Comment thread openspec/changes/generic-mapper/specs/custom-mapper-config/spec.md Outdated
@jarhodes314 jarhodes314 force-pushed the proposal/generic-mapper branch from 96b1101 to 80e8738 Compare February 26, 2026 15:34
@jarhodes314 jarhodes314 requested a review from rina23q as a code owner February 26, 2026 15:34
@jarhodes314 jarhodes314 force-pushed the proposal/generic-mapper branch from f04ef32 to 90ce6c9 Compare February 26, 2026 16:37
@jarhodes314 jarhodes314 changed the title Proposal for generic/custom cloud mapper Initial attempt at a generic/custom cloud mapper Feb 26, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 26, 2026

Robot Results

✅ Passed ❌ Failed ⏭️ Skipped Total Pass % ⏱️ Duration
894 0 3 894 100 2h14m7.402502999s

use tedge_config::models::HostPort;
use tedge_config::models::MQTT_TLS_PORT;

/// Parsed custom mapper `tedge.toml` configuration.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the contents of this file are read from the mapper.* namespace, wondering if naming the config file mapper.toml instead of tedge.toml would make it easier for the users even us to easily do that association in our heads. I might even go one step further and change the config.* namespace into `tedge.*.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have now renamed tedge.toml to mapper.toml following discussion with @reubenmiller on Friday

I might even go one step further and change the config.* namespace into `tedge.*.

That's an interesting thought, I'll give it some consideration

Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
…en deciding if mapper.toml is empty

Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
- Make cert CN take precedence over manually configured device.id, like
we do for other clouds
- Make device.id required for a custom mapper bridge connection

Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
${mapper.*} variables in bridge rules were expanded against an empty table,
causing them to always produce empty strings. The fix threads the fully
resolved mapper configuration through to the rule expansion step for both
built-in (c8y) and custom mappers.

To support this, EffectiveMapperConfig gains two new methods:
 - `to_template_table()`: builds the TOML table used for ${mapper.*}
    expansion, overlaying schema-resolved values (cert CN, tedge.toml
    fallbacks) onto the raw mapper.toml table so non-schema keys like
    bridge.topic_prefix are also reachable
 - `get(key) -> ConfigGetResult`: source-aware key lookup distinguishing
    Value / NotSet / UnknownKey, used by `tedge mapper config get`

`resolve_effective_config` gains two new parameters: an optional overlay
table (built-in mappers pass their full serialised config so CLI commands
reflect real runtime values) and an optional JSON schema override (to
distinguish NotSet from UnknownKey for keys absent from the TOML).

A new `resolve_effective_mapper_config` function on the c8y mapper
serialises the full C8y reader as a base overlay and JSON schema, shared
between bridge rule loading and the CLI.

`tedge mapper config get` is rewritten to use the new API: it handles
built-in and custom mappers uniformly, produces precise error messages
("Key not set" vs "Unknown key", with a cert-specific hint for device.id),
and prints a `tedge config set` suggestion for built-in mapper keys. The
execute body is extracted into `run(out, err)` so tests can assert on the
actual printed output and stderr annotations.

`cloud_type` in mapper.toml is now parsed as `tedge_config::models::CloudType`
rather than a free-form string, so unknown values are a hard error at
parse time.

A new startup warning is emitted for mapper directories that contain a
`bridge/` subdirectory but no `mapper.toml` — these mappers will always
fail to start and the warning points the user at the fix.

`resolve_cloud` is deduplicated into cli::common, shared by bridge and
mapper CLI code.

Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
Copy link
Copy Markdown
Contributor

@reubenmiller reubenmiller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really nice PR and brings some really exciting features

@jarhodes314 jarhodes314 added this pull request to the merge queue Mar 26, 2026
Merged via the queue into thin-edge:main with commit d8dd6a9 Mar 26, 2026
34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

theme:cloud Theme: Cloud related topics which aren't related to any specific cloud mapper

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants