A Dart client for the DMTF Redfish API — the HTTPS interface most enterprise server BMCs have provided since roughly 2016.
Written against what BMCs actually send, which is not always what the specification says they will. Extracted from ServerBox, where it reaches a server's management controller when the host operating system is not answering.
Pure Dart. No Flutter dependency.
Not published to pub.dev yet. The API is still moving. Use a git or path dependency.
Protocol
GET,POSTandPATCH, withIf-Matchfrom theETagafetchreturned202 Acceptedcomes back as a task to poll, not as a result — see below- Sessions by default, basic auth on request, and basic automatically when the service offers no session collection
$expandwhere a service supports it, as an optimisation and never a requirement
Resources
- Discovery: walks
SystemsandChassisrather than building paths, because the ids differ per vendor ComputerSystem: power state, model, serial, BIOS version, healthComputerSystem.Reset, with the reset type negotiated against what the service says it allowsChassis: sensors, out of either of the two models Redfish definesManager: the controller's own firmware, clock, network interfaces and reset — which reboots the BMC and not the serverLogService/LogEntry: the event log, with the SEL picked out of the several a manager publishesBoot: reading an override and building the PATCH that sets or clears one- Inventory: processors, memory, drives, reduced to one shape
Not covered. Virtual media, UpdateService and firmware upload, AccountService, EventService subscriptions, message registries (a MessageId with no Message is passed through rather than resolved), Swordfish.
Any modifying request may answer 202 with a task doing the work elsewhere.
A client that treats every 2xx as completion reports success at the moment the
work was queued.
final outcome = await client.post(request.target, request.body);
if (outcome case RedfishAccepted()) {
final task = await client.awaitTask(outcome);
if (!task.state.isSuccess) print(task.messages);
}awaitTask returns the last state it saw. A task still running when the wait
ends comes back running rather than as a failure: the work has not stopped,
only the watching has.
The split that matters is between the parts that need a BMC and the parts that do not.
resources.dart JSON -> models. No IO.
sensors.dart Readings, out of either sensor model. No IO.
discovery.dart Walking a service. Takes a RedfishTransport.
client.dart The only thing that opens a socket.
cert_pin.dart TLS trust on first use, in the two halves the platform forces.
Everything above client takes a decoded JSON map and returns a model, so a vendor difference can be reproduced from a saved response without the hardware that produced it. That is not tidiness — it is the only way the differences in doc/vendors.md stay honest.
BMCs ship self-signed certificates, so there is nothing for a CA to vouch for. This library does not offer a way to accept any certificate. It offers trust on first review:
// Reads the certificate and sends nothing. Put it to a person.
final info = await fetchServerCert('10.0.0.9', 443);
print(info.prettyFingerprint);
final client = RedfishClient(
baseUrl: 'https://10.0.0.9',
user: 'ADMIN',
password: '...',
pinnedCertSha256: info.fingerprint, // null refuses everything
);The two halves are separate because the platform forces it: badCertificateCallback is bool Function(...), so nothing there can wait for an answer. Trusting whatever appears on the first request would be trust-on-first-use where the use already carries a password.
final client = RedfishClient(
baseUrl: 'https://10.0.0.9',
user: 'ADMIN',
password: '...',
pinnedCertSha256: pin,
);
try {
final topology = await RedfishDiscovery(client).run();
print(topology.system?.powerState);
final request = ResetRequest.build(topology.system!, PowerIntent.gracefulShutdown);
if (request != null) {
await client.post(request.target, request.body);
}
} finally {
await client.close(); // releases the session
}ResetRequest.build returns null when the service allows nothing that satisfies the intent. That is a real answer: an operation with nothing behind it should not be offered rather than offered and failing when pressed.
mockups/ holds Redfish trees that can be served back with DMTF's Redfish-Mockup-Server:
tool/serve_mockup.sh # DMTF's public-rackmount1
tool/serve_mockup.sh mockups/real/foo # a tree captured from real firmwareContributions of mockups are more useful than bug reports. DMTF's Redfish-Mockup-Creator captures a live service into a folder — no credentials leave your network, and the result becomes a permanent fixture here. Scrub serial numbers and network configuration first.
Note the difference between the two kinds: DMTF's published mockups are reference examples and state that they "do not represent actual implementations". They test conformance to the specification. Mockups captured from real firmware are what catch the things in doc/vendors.md, none of which any specification predicts.
Apache-2.0. See LICENSE and NOTICE.