From 2af003f1b5e4b40601dd209c8372a92ad897e556 Mon Sep 17 00:00:00 2001 From: GHA Date: Wed, 6 Dec 2023 11:56:05 +0000 Subject: [PATCH] update 7948cff549e4092635e7ba0e902dc2b0e8434862 --- .buildinfo | 4 + .nojekyll | 0 _images/summary.png | Bin 0 -> 33388 bytes _sources/api.rst.txt | 82 + _sources/index.md.txt | 16 + _sources/introduction.md.txt | 14 + _sources/rdfm_android_device_client.md.txt | 121 + _sources/rdfm_artifact.md.txt | 85 + _sources/rdfm_linux_device_client.md.txt | 68 + _sources/rdfm_manager.md.txt | 201 ++ _sources/rdfm_mgmt_server.md.txt | 343 +++ _sources/rdfm_ota_manual.md.txt | 113 + _sources/server_operation.md.txt | 111 + _sources/system_overview.md.txt | 44 + .../0053ba6958e79f26751eabb555bd73d0.woff2 | Bin 0 -> 4728 bytes .../029e176ad602329b4434892101db9cf3.woff2 | Bin 0 -> 6044 bytes .../07ff82964967feebb9c96288e0e0df05.woff2 | Bin 0 -> 13588 bytes .../0948409a22b5979aa7e1ec20da9e61f1.woff2 | Bin 0 -> 5604 bytes .../0a0ad0eae50e549ecd713b9ad417f1a1.woff2 | Bin 0 -> 4888 bytes .../0b68e8634c96265eb32a0c769416b5b0.woff2 | Bin 0 -> 5928 bytes .../0d1b73eee266eabb2cff35dfa4ce25a3.woff2 | Bin 0 -> 16812 bytes .../0e1f73c6737cdf273efb4b79504e4c0a.woff2 | Bin 0 -> 13076 bytes .../0e326670106c8eb6a11a8c30734ecfc8.ttf | Bin 0 -> 23124 bytes .../0ec3cc19652785204ea2e322330f0f1b.woff2 | Bin 0 -> 16164 bytes .../0f303f31706d39866cced9dcc17b61fb.woff2 | Bin 0 -> 15764 bytes .../101522bafe9c61c68698ecc784607772.woff2 | Bin 0 -> 9712 bytes .../10b31f4cad9ea78d43449886bfbb88ac.woff2 | Bin 0 -> 11804 bytes .../1181a8e619707033241139715eca64c6.woff2 | Bin 0 -> 9672 bytes .../122802d03aed4bf8cd6a03997a97aca4.woff2 | Bin 0 -> 6020 bytes .../1383417807f7965daaf94e7c497dcddb.woff2 | Bin 0 -> 7704 bytes .../144860ed1e48e186f08997e6388a9c3f.woff2 | Bin 0 -> 1512 bytes .../1488146d8b2e9859d6c90e6c2b48f7ef.woff2 | Bin 0 -> 6340 bytes .../1512b579343c6b61c7523cdd838d8328.ttf | Bin 0 -> 23416 bytes .../1c9cc76fd52238330f0aabac35acd2ca.woff2 | Bin 0 -> 6936 bytes .../1f1481679a64a39f3427547aa1b13f0f.woff2 | Bin 0 -> 5032 bytes .../2096d27efc16cbdd79183bf295c8ebde.ttf | Bin 0 -> 21352 bytes .../20dc200cc43ab904876fb0c1697ebe39.woff2 | Bin 0 -> 1480 bytes .../214adfc289a2f2af8b0008c59ed0c7f2.woff2 | Bin 0 -> 4652 bytes .../21953b998bab09c1f60c599caee56378.woff2 | Bin 0 -> 7696 bytes .../22aadc77cafa07b2db9ed560d0320616.woff2 | Bin 0 -> 13200 bytes .../2325b97b584755067ea4f7f56ee05430.woff2 | Bin 0 -> 8348 bytes .../2550c2e2d8495c3ed2d4d52f824374f1.woff2 | Bin 0 -> 7040 bytes .../255cf41e0317d95e3992683a76ef28a8.woff2 | Bin 0 -> 4976 bytes .../25c52b9af13f0d1b10719f5289e8c803.woff2 | Bin 0 -> 7476 bytes .../2781e9e7c3f369b8fc7965e679b17b60.woff2 | Bin 0 -> 11756 bytes .../28e6b81b1bc1964707edd4179e4268f5.ttf | Bin 0 -> 23416 bytes .../2a8c422bef4a7099e99dbf0e61ed5e49.woff2 | Bin 0 -> 7460 bytes .../2aadfad5aee7ceeaf4eb0924efabe5b4.ttf | Bin 0 -> 21772 bytes .../2c0f74be498d2da814c0a84dd6833f70.woff2 | Bin 0 -> 15092 bytes .../2e10480d4154762bc7c8fbb40877e104.woff2 | Bin 0 -> 5928 bytes .../2ea7a97b7c976b121112a088eb398561.woff2 | Bin 0 -> 7700 bytes .../2f5c32f094829c0278bce28fe2bbe074.ttf | Bin 0 -> 23204 bytes .../2f7c3c315334a99574ee4ceb21af654d.woff2 | Bin 0 -> 7544 bytes .../302b0425bf5ea66f37a822a61d723adc.ttf | Bin 0 -> 25112 bytes .../3177dacffeac1eb4102852811ae4a2c7.woff2 | Bin 0 -> 6236 bytes .../3254c528e2ab56454a9f22191035c5fe.ttf | Bin 0 -> 21356 bytes .../32c8a74ac0816253d69a7cc68a60986d.woff2 | Bin 0 -> 12764 bytes .../33c5d27ca0eaeb12ebe728ae2fc7106d.woff2 | Bin 0 -> 15360 bytes .../36e39c6463ae1c71c71e69c05e593e1b.woff2 | Bin 0 -> 4588 bytes .../3728fbdd191d75bad5b83a838dfe2fc1.woff2 | Bin 0 -> 9840 bytes .../38f3ee1f96b758f95672c632d8759594.ttf | Bin 0 -> 23172 bytes .../392ff374142585f7b886ee1fe66e686e.woff2 | Bin 0 -> 6560 bytes .../3a38c967413f7bce36d3baefc321aade.woff2 | Bin 0 -> 5468 bytes .../3c23eb02de6b34e30f18cfb7167abd81.woff2 | Bin 0 -> 11872 bytes .../3c505383d37d2078648e37868bbd1fad.woff2 | Bin 0 -> 14684 bytes .../3cf78ad3bcd1324e10a4acdc34bfc4a1.woff2 | Bin 0 -> 17552 bytes .../3f1918538864f9681d47a4538d48289c.woff2 | Bin 0 -> 5876 bytes .../4039566f251699c4b421ed1a38a59b24.woff2 | Bin 0 -> 4688 bytes .../4207cbc8cb7bc2cbd0bcce565298cbbc.woff2 | Bin 0 -> 9768 bytes .../43358c04243de546caddd0898dbf0757.woff2 | Bin 0 -> 14004 bytes .../435e4b7f9f250d9d9243d4754799fc96.woff2 | Bin 0 -> 15000 bytes .../437939342255944b82a49f916404c5fc.woff2 | Bin 0 -> 6516 bytes .../455c2c1af0a2bf20047a1864d7d7c174.woff2 | Bin 0 -> 7120 bytes .../47aa3bfad6cb9e2d63abdd58f4e6ce4f.woff2 | Bin 0 -> 9576 bytes .../495d38d4b9741e8aa4204002414069e2.woff2 | Bin 0 -> 9628 bytes .../4c815fdc869f885520f7c8eae6730edf.woff2 | Bin 0 -> 16608 bytes .../4ec57f2a80b91090971b83970230ca09.woff2 | Bin 0 -> 5548 bytes .../4f17f22fc6bff4f3333ccf7ed7126e6d.woff2 | Bin 0 -> 1464 bytes .../4f93c2808e3b69e525c118074e5de31f.woff2 | Bin 0 -> 14184 bytes .../50aacf068f685be0dd903a91d5bab7d8.woff2 | Bin 0 -> 1508 bytes .../51f3f41805329fb8341beb56ded833ea.woff2 | Bin 0 -> 5468 bytes .../52f28cb4d065b4adfa78df4f9559c639.woff2 | Bin 0 -> 7392 bytes .../555ceea3a65ffbbecf8b7e6d04966c7f.woff2 | Bin 0 -> 14128 bytes .../5989ef3a21d7f252337ab3326f78bde7.woff2 | Bin 0 -> 4780 bytes .../5b6377da4c959db6d4b22738a27f1bee.woff2 | Bin 0 -> 1432 bytes .../5ce47d5195e59af38114d0b70217baf2.woff2 | Bin 0 -> 14024 bytes .../5d7ff31ac7bf945e8d61878f8a941239.woff2 | Bin 0 -> 1460 bytes .../5dc0e4b14e903ba7f45c581df7402b3f.woff2 | Bin 0 -> 14072 bytes .../60eb682678bbea5e8ad71f66f2f65536.woff2 | Bin 0 -> 10284 bytes .../63111d307c01b52ffccf7b0319cb7917.woff2 | Bin 0 -> 1540 bytes .../638764dc2513deb09c55fc025f6dd36c.woff2 | Bin 0 -> 9180 bytes .../63f4b74ebf127dbeb033126ea988f54e.woff2 | Bin 0 -> 7520 bytes .../64a6b4e954cf84685cbf8de77eb47344.woff2 | Bin 0 -> 12572 bytes .../661d4b208656c006e7aab58acf778485.woff2 | Bin 0 -> 17336 bytes .../6725a7e91680edd1cdc9ed5c26ac05fd.woff2 | Bin 0 -> 14224 bytes .../6a84eeee6a25e7c9a8a03191007a6720.woff2 | Bin 0 -> 9644 bytes .../6ac1ee292434fac2313c42b0dfb7897c.ttf | Bin 0 -> 23488 bytes .../6ad3f6bbe6220cc476a0d3c731d3fb04.ttf | Bin 0 -> 23672 bytes .../6be97ca17228a69c406231d89c003194.woff2 | Bin 0 -> 17032 bytes .../6de03a64aa8100032abc6e836b3ed803.ttf | Bin 0 -> 23520 bytes .../6deb20301c65a96db17c433ad0cf8158.woff2 | Bin 0 -> 10640 bytes .../6f8d857c5a8545e67de6b60aa0fe5c33.woff2 | Bin 0 -> 12740 bytes .../713780d8b30bda5583052ea847cdcb4f.woff2 | Bin 0 -> 7016 bytes .../71e06579279fba7436d58a1c49288909.ttf | Bin 0 -> 25364 bytes .../765bd4a97597a4d7781193793477a6cd.ttf | Bin 0 -> 25224 bytes .../76945c7494c20515bb45d1dedab8f706.woff2 | Bin 0 -> 10428 bytes .../76da333ab59c6d625cabfb0768f82b4a.woff2 | Bin 0 -> 1464 bytes .../770518db51bed1e082feecc532cfcbf8.woff2 | Bin 0 -> 7404 bytes .../77b24796a3d4ab521f66765651875338.woff2 | Bin 0 -> 5560 bytes .../77ff81100e5a1db3d925f713660700ad.woff2 | Bin 0 -> 4748 bytes .../78a9265759e7b861a1639a36f4c01d04.woff2 | Bin 0 -> 13860 bytes .../7af61b2367eba2b1852e837c46a75696.woff2 | Bin 0 -> 12848 bytes .../7b63598dcc2a26583b82594bd0e36d5b.woff2 | Bin 0 -> 5760 bytes .../7b8c2179b6b778308d2ff39bdb82e926.woff2 | Bin 0 -> 6012 bytes .../7e262106f82cc52663e403f5b73795bb.woff2 | Bin 0 -> 15752 bytes .../7f1c829b0c90fd664a03bb714a74f7d3.woff2 | Bin 0 -> 11800 bytes .../7fa86b886bee5d6ab420a8e89b9f3052.ttf | Bin 0 -> 23724 bytes .../8007dfe835cfb201b8caaa9651098588.woff2 | Bin 0 -> 1428 bytes .../83614c36460a4a9734968789cb535de7.woff2 | Bin 0 -> 5020 bytes .../84e959dd07f302392f0ffd86f87db888.ttf | Bin 0 -> 21452 bytes .../85a41b80c5fdc14e3dc48636a30d87dd.woff2 | Bin 0 -> 5884 bytes .../870e5928dd14fcfe0ce9386107666774.woff2 | Bin 0 -> 6040 bytes .../8898c4b754d5d96c1a5e1b1d54100554.woff2 | Bin 0 -> 6404 bytes .../89b4f174a5a728d2d8c85b87990c9ab4.ttf | Bin 0 -> 23420 bytes .../8a8dca39f24b52e89e6fd6dcd8b6dd32.woff2 | Bin 0 -> 7476 bytes .../8aa562790559d61dd5178a88a296d70f.ttf | Bin 0 -> 23252 bytes .../8c3798e37724f71bc0c63c44a5307413.woff2 | Bin 0 -> 7012 bytes .../8c49ed8b472d38d3985ec9bbbccea601.ttf | Bin 0 -> 21560 bytes .../8e48cf20cf9f9e5feb7197c79028132b.woff2 | Bin 0 -> 14688 bytes .../9095d663e4d450059bcc2260bb75cd62.woff2 | Bin 0 -> 4696 bytes .../90ebb29b5cffa197b184773983ba7e91.woff2 | Bin 0 -> 13188 bytes .../93b6c99d936df38895a0d95e3ffea2fd.woff2 | Bin 0 -> 9556 bytes .../9582ced8a675bf267cc7ac392a86413e.woff2 | Bin 0 -> 12704 bytes .../99be4d68845d66c27c7f7d3a48687b66.woff2 | Bin 0 -> 7616 bytes .../99cf36e763be9cce7b4c59b91841af58.woff2 | Bin 0 -> 8280 bytes .../9a9bf2d91ebbb1b96eab8eb0b0514bcc.woff2 | Bin 0 -> 4896 bytes .../9bcbc88b33b2efc2aee821b831499f1c.woff2 | Bin 0 -> 8320 bytes .../9c9be791a58af8a04c611ca1d13f51c6.woff2 | Bin 0 -> 5088 bytes .../9fdb12ceee3a402d3a54afe354552459.woff2 | Bin 0 -> 9700 bytes .../a6933e678530b263486fa7b185a449ca.woff2 | Bin 0 -> 10292 bytes .../a6caf7b9888eb0c382948c1ca5e8bebb.woff2 | Bin 0 -> 16676 bytes .../a70ff2592da5e3453943f727633aff54.woff2 | Bin 0 -> 6344 bytes .../aa28d99c7db60ad23f96a5c317615c42.woff2 | Bin 0 -> 13696 bytes .../aab05142e0e2dadf7df633e061e612ad.woff2 | Bin 0 -> 14136 bytes .../ab03beb9091fa15ce4e783199e076bc6.woff2 | Bin 0 -> 8300 bytes .../ac848474638236e67a64bc654fb18de0.ttf | Bin 0 -> 21464 bytes .../acaac043ca238f0e56e61864456777fa.woff2 | Bin 0 -> 12620 bytes .../aeed0e51b0bac7c89e5c7e6cf086d7e0.woff2 | Bin 0 -> 14968 bytes .../b019538234514166ec7665359d097403.woff2 | Bin 0 -> 15920 bytes .../b076e86301cbee8c5c9aef51863a9c0a.woff2 | Bin 0 -> 11796 bytes .../b19ac4e57f2a56639eebd1c35319e5a7.woff2 | Bin 0 -> 17060 bytes .../b4d3c40a77fd9e35a881a79077957055.woff2 | Bin 0 -> 14172 bytes .../b4e42731e8d667ae87c3450c345754ae.woff2 | Bin 0 -> 5996 bytes .../b57a5ada789f195d5d42f4073a6cf313.woff2 | Bin 0 -> 9960 bytes .../b5b4146d87e5d22d0a4e0d04f3ee5626.woff2 | Bin 0 -> 1512 bytes .../b7ef2cd1159a8cbfd271ff2abe07f237.woff2 | Bin 0 -> 15344 bytes .../b93199bb6f964f190f4da04ecdbaf5a4.woff2 | Bin 0 -> 15076 bytes .../bb8007225d94a099cddbade7ea904667.woff2 | Bin 0 -> 17508 bytes .../bc67bba106323289ea3eda0826de1912.ttf | Bin 0 -> 25404 bytes .../bcd47c2f3649cfcaa86a08fb741255d6.woff2 | Bin 0 -> 13944 bytes .../bd0efe13f0d9d591b337ddc7f289f494.woff2 | Bin 0 -> 15204 bytes .../bd51fb0ca67e64c809ffcf7e1370f969.woff2 | Bin 0 -> 8420 bytes .../bdbb6b52604c2451fdcba9cdfd44f4e1.woff2 | Bin 0 -> 5972 bytes .../bf2ad3287f13eb7076cccb516ec2986f.ttf | Bin 0 -> 23456 bytes .../bfd1a0c9c783e84595589f33e1828a57.woff2 | Bin 0 -> 12832 bytes .../c13b34dd5b6a35b309944b61c91b2ace.woff2 | Bin 0 -> 8408 bytes .../c22066c14662d6c80415ae04c5dd9d51.woff2 | Bin 0 -> 14780 bytes .../c28a41f656599f6694528b5463c6a445.woff2 | Bin 0 -> 12980 bytes .../c6dc61b627bbc5af9130518297bd4f17.ttf | Bin 0 -> 23720 bytes .../c8a9fd4eab4e83382cc66fde70911b41.woff2 | Bin 0 -> 10076 bytes .../ca7eea0cf248d6e8442c01074765bd33.woff2 | Bin 0 -> 5388 bytes .../cadfb311297a9362b07fab73934b432a.ttf | Bin 0 -> 25380 bytes .../cbfd26d5bcf084ee407a0b2b7599e84b.woff2 | Bin 0 -> 9524 bytes .../ccdebed88064e470c15f37c432922e57.woff2 | Bin 0 -> 16024 bytes .../cce2217cc8323fe49789adefb3596291.woff2 | Bin 0 -> 12980 bytes .../cd3d1f17e048e2116f438bd7157baccf.woff2 | Bin 0 -> 9504 bytes .../d07f561ba87d93460742b060727d9e0d.woff2 | Bin 0 -> 10276 bytes .../d368cf5bed7856dbafa2af36b51acb9c.woff2 | Bin 0 -> 1484 bytes .../d422317033deb87342a5e56c7be67458.ttf | Bin 0 -> 25444 bytes .../d6f9cdf1a40893111566fcdee3bbe5a9.woff2 | Bin 0 -> 14060 bytes .../d98f35e926c11f3d5c0c8e3205d43907.ttf | Bin 0 -> 25360 bytes .../d9e6a498dac7e9e91f6e0b4f8930eba0.woff2 | Bin 0 -> 10532 bytes .../da6cd48e6dad1888fccc91735e7522f7.woff2 | Bin 0 -> 10652 bytes .../daf12b5f1889502004bba85ad71f9fa4.woff2 | Bin 0 -> 7540 bytes .../daf51ab540602b2d0b87646621637bac.woff2 | Bin 0 -> 7112 bytes .../db0424fb67fb52e7e538490240cc7fb9.woff2 | Bin 0 -> 17368 bytes .../dc25cbf4baaf778bd8ae78fbc0e79479.woff2 | Bin 0 -> 14052 bytes .../dd719f1662079ce6a61260f9af972379.woff2 | Bin 0 -> 9876 bytes .../de018865c95896bb57265fc97c48ebd7.woff2 | Bin 0 -> 8108 bytes .../e33716333704ab19fdf9989e072ad49a.woff2 | Bin 0 -> 5928 bytes .../e56cc9fb5272752b78f144b4be43175d.woff2 | Bin 0 -> 7608 bytes .../e704ef18719c08839bc99a32437ef0f8.woff2 | Bin 0 -> 16700 bytes .../e99627cd27de169d23ece4573006af2a.woff2 | Bin 0 -> 15304 bytes .../ef8f0236a7e8b46bc9d642ecf4ab0cb7.woff2 | Bin 0 -> 1500 bytes .../f154d62b4879af7a22895af7a4ef03f0.woff2 | Bin 0 -> 10276 bytes .../f17ee050ada0453f3bd07bc466c2dde2.woff2 | Bin 0 -> 10564 bytes .../f265cee675c0e5b2d6ab263d0edcc754.woff2 | Bin 0 -> 14856 bytes .../f2f69e8cd15fdd15a4244c95ec8a8514.woff2 | Bin 0 -> 10344 bytes .../f534242dea2255c25b9d05c2371986e3.woff2 | Bin 0 -> 6380 bytes .../f53f3b5a15d717b6d21d7885285e90ed.woff2 | Bin 0 -> 12864 bytes .../f55dac651a40fce74a5cf5728d9f8ffc.woff2 | Bin 0 -> 9784 bytes .../f5aebdfea35d1e7656ef4acc5db1f243.woff2 | Bin 0 -> 15860 bytes .../f5f971e9640a9eb86ef553a7e7e999c7.woff2 | Bin 0 -> 6048 bytes .../f6734f8177112c0839b961f96d813fcb.woff2 | Bin 0 -> 15744 bytes .../f75911313e1c7802c23345ab57e754d8.woff2 | Bin 0 -> 15740 bytes .../fb17f56622e45dd4ecee00bb5c63cd2b.woff2 | Bin 0 -> 4580 bytes .../fb1aaa90783b8cb9375265abeb91b153.woff2 | Bin 0 -> 15336 bytes .../fc66f942651a9fe1a598770d3d896529.woff2 | Bin 0 -> 11824 bytes _static/language_data.js | 199 ++ ..._immaterial_theme.1b5b7a2d5891aec19.min.js | 27 + ...immaterial_theme.af531f03affe68837.min.css | 4 + _static/white.svg | 54 + api.html | 2090 +++++++++++++++++ genindex.html | 457 ++++ http-routingtable.html | 464 ++++ index.html | 556 +++++ introduction.html | 512 ++++ objects.inv | 7 + rdfm_android_device_client.html | 723 ++++++ rdfm_artifact.html | 661 ++++++ rdfm_linux_device_client.html | 644 +++++ rdfm_manager.html | 772 ++++++ rdfm_mgmt_server.html | 945 ++++++++ rdfm_ota_manual.html | 701 ++++++ searchindex.js | 1 + server_operation.html | 667 ++++++ system_overview.html | 568 +++++ 227 files changed, 11254 insertions(+) create mode 100644 .buildinfo create mode 100644 .nojekyll create mode 100644 _images/summary.png create mode 100644 _sources/api.rst.txt create mode 100644 _sources/index.md.txt create mode 100644 _sources/introduction.md.txt create mode 100644 _sources/rdfm_android_device_client.md.txt create mode 100644 _sources/rdfm_artifact.md.txt create mode 100644 _sources/rdfm_linux_device_client.md.txt create mode 100644 _sources/rdfm_manager.md.txt create mode 100644 _sources/rdfm_mgmt_server.md.txt create mode 100644 _sources/rdfm_ota_manual.md.txt create mode 100644 _sources/server_operation.md.txt create mode 100644 _sources/system_overview.md.txt create mode 100644 _static/fonts/0053ba6958e79f26751eabb555bd73d0.woff2 create mode 100644 _static/fonts/029e176ad602329b4434892101db9cf3.woff2 create mode 100644 _static/fonts/07ff82964967feebb9c96288e0e0df05.woff2 create mode 100644 _static/fonts/0948409a22b5979aa7e1ec20da9e61f1.woff2 create mode 100644 _static/fonts/0a0ad0eae50e549ecd713b9ad417f1a1.woff2 create mode 100644 _static/fonts/0b68e8634c96265eb32a0c769416b5b0.woff2 create mode 100644 _static/fonts/0d1b73eee266eabb2cff35dfa4ce25a3.woff2 create mode 100644 _static/fonts/0e1f73c6737cdf273efb4b79504e4c0a.woff2 create mode 100644 _static/fonts/0e326670106c8eb6a11a8c30734ecfc8.ttf create mode 100644 _static/fonts/0ec3cc19652785204ea2e322330f0f1b.woff2 create mode 100644 _static/fonts/0f303f31706d39866cced9dcc17b61fb.woff2 create mode 100644 _static/fonts/101522bafe9c61c68698ecc784607772.woff2 create mode 100644 _static/fonts/10b31f4cad9ea78d43449886bfbb88ac.woff2 create mode 100644 _static/fonts/1181a8e619707033241139715eca64c6.woff2 create mode 100644 _static/fonts/122802d03aed4bf8cd6a03997a97aca4.woff2 create mode 100644 _static/fonts/1383417807f7965daaf94e7c497dcddb.woff2 create mode 100644 _static/fonts/144860ed1e48e186f08997e6388a9c3f.woff2 create mode 100644 _static/fonts/1488146d8b2e9859d6c90e6c2b48f7ef.woff2 create mode 100644 _static/fonts/1512b579343c6b61c7523cdd838d8328.ttf create mode 100644 _static/fonts/1c9cc76fd52238330f0aabac35acd2ca.woff2 create mode 100644 _static/fonts/1f1481679a64a39f3427547aa1b13f0f.woff2 create mode 100644 _static/fonts/2096d27efc16cbdd79183bf295c8ebde.ttf create mode 100644 _static/fonts/20dc200cc43ab904876fb0c1697ebe39.woff2 create mode 100644 _static/fonts/214adfc289a2f2af8b0008c59ed0c7f2.woff2 create mode 100644 _static/fonts/21953b998bab09c1f60c599caee56378.woff2 create mode 100644 _static/fonts/22aadc77cafa07b2db9ed560d0320616.woff2 create mode 100644 _static/fonts/2325b97b584755067ea4f7f56ee05430.woff2 create mode 100644 _static/fonts/2550c2e2d8495c3ed2d4d52f824374f1.woff2 create mode 100644 _static/fonts/255cf41e0317d95e3992683a76ef28a8.woff2 create mode 100644 _static/fonts/25c52b9af13f0d1b10719f5289e8c803.woff2 create mode 100644 _static/fonts/2781e9e7c3f369b8fc7965e679b17b60.woff2 create mode 100644 _static/fonts/28e6b81b1bc1964707edd4179e4268f5.ttf create mode 100644 _static/fonts/2a8c422bef4a7099e99dbf0e61ed5e49.woff2 create mode 100644 _static/fonts/2aadfad5aee7ceeaf4eb0924efabe5b4.ttf create mode 100644 _static/fonts/2c0f74be498d2da814c0a84dd6833f70.woff2 create mode 100644 _static/fonts/2e10480d4154762bc7c8fbb40877e104.woff2 create mode 100644 _static/fonts/2ea7a97b7c976b121112a088eb398561.woff2 create mode 100644 _static/fonts/2f5c32f094829c0278bce28fe2bbe074.ttf create mode 100644 _static/fonts/2f7c3c315334a99574ee4ceb21af654d.woff2 create mode 100644 _static/fonts/302b0425bf5ea66f37a822a61d723adc.ttf create mode 100644 _static/fonts/3177dacffeac1eb4102852811ae4a2c7.woff2 create mode 100644 _static/fonts/3254c528e2ab56454a9f22191035c5fe.ttf create mode 100644 _static/fonts/32c8a74ac0816253d69a7cc68a60986d.woff2 create mode 100644 _static/fonts/33c5d27ca0eaeb12ebe728ae2fc7106d.woff2 create mode 100644 _static/fonts/36e39c6463ae1c71c71e69c05e593e1b.woff2 create mode 100644 _static/fonts/3728fbdd191d75bad5b83a838dfe2fc1.woff2 create mode 100644 _static/fonts/38f3ee1f96b758f95672c632d8759594.ttf create mode 100644 _static/fonts/392ff374142585f7b886ee1fe66e686e.woff2 create mode 100644 _static/fonts/3a38c967413f7bce36d3baefc321aade.woff2 create mode 100644 _static/fonts/3c23eb02de6b34e30f18cfb7167abd81.woff2 create mode 100644 _static/fonts/3c505383d37d2078648e37868bbd1fad.woff2 create mode 100644 _static/fonts/3cf78ad3bcd1324e10a4acdc34bfc4a1.woff2 create mode 100644 _static/fonts/3f1918538864f9681d47a4538d48289c.woff2 create mode 100644 _static/fonts/4039566f251699c4b421ed1a38a59b24.woff2 create mode 100644 _static/fonts/4207cbc8cb7bc2cbd0bcce565298cbbc.woff2 create mode 100644 _static/fonts/43358c04243de546caddd0898dbf0757.woff2 create mode 100644 _static/fonts/435e4b7f9f250d9d9243d4754799fc96.woff2 create mode 100644 _static/fonts/437939342255944b82a49f916404c5fc.woff2 create mode 100644 _static/fonts/455c2c1af0a2bf20047a1864d7d7c174.woff2 create mode 100644 _static/fonts/47aa3bfad6cb9e2d63abdd58f4e6ce4f.woff2 create mode 100644 _static/fonts/495d38d4b9741e8aa4204002414069e2.woff2 create mode 100644 _static/fonts/4c815fdc869f885520f7c8eae6730edf.woff2 create mode 100644 _static/fonts/4ec57f2a80b91090971b83970230ca09.woff2 create mode 100644 _static/fonts/4f17f22fc6bff4f3333ccf7ed7126e6d.woff2 create mode 100644 _static/fonts/4f93c2808e3b69e525c118074e5de31f.woff2 create mode 100644 _static/fonts/50aacf068f685be0dd903a91d5bab7d8.woff2 create mode 100644 _static/fonts/51f3f41805329fb8341beb56ded833ea.woff2 create mode 100644 _static/fonts/52f28cb4d065b4adfa78df4f9559c639.woff2 create mode 100644 _static/fonts/555ceea3a65ffbbecf8b7e6d04966c7f.woff2 create mode 100644 _static/fonts/5989ef3a21d7f252337ab3326f78bde7.woff2 create mode 100644 _static/fonts/5b6377da4c959db6d4b22738a27f1bee.woff2 create mode 100644 _static/fonts/5ce47d5195e59af38114d0b70217baf2.woff2 create mode 100644 _static/fonts/5d7ff31ac7bf945e8d61878f8a941239.woff2 create mode 100644 _static/fonts/5dc0e4b14e903ba7f45c581df7402b3f.woff2 create mode 100644 _static/fonts/60eb682678bbea5e8ad71f66f2f65536.woff2 create mode 100644 _static/fonts/63111d307c01b52ffccf7b0319cb7917.woff2 create mode 100644 _static/fonts/638764dc2513deb09c55fc025f6dd36c.woff2 create mode 100644 _static/fonts/63f4b74ebf127dbeb033126ea988f54e.woff2 create mode 100644 _static/fonts/64a6b4e954cf84685cbf8de77eb47344.woff2 create mode 100644 _static/fonts/661d4b208656c006e7aab58acf778485.woff2 create mode 100644 _static/fonts/6725a7e91680edd1cdc9ed5c26ac05fd.woff2 create mode 100644 _static/fonts/6a84eeee6a25e7c9a8a03191007a6720.woff2 create mode 100644 _static/fonts/6ac1ee292434fac2313c42b0dfb7897c.ttf create mode 100644 _static/fonts/6ad3f6bbe6220cc476a0d3c731d3fb04.ttf create mode 100644 _static/fonts/6be97ca17228a69c406231d89c003194.woff2 create mode 100644 _static/fonts/6de03a64aa8100032abc6e836b3ed803.ttf create mode 100644 _static/fonts/6deb20301c65a96db17c433ad0cf8158.woff2 create mode 100644 _static/fonts/6f8d857c5a8545e67de6b60aa0fe5c33.woff2 create mode 100644 _static/fonts/713780d8b30bda5583052ea847cdcb4f.woff2 create mode 100644 _static/fonts/71e06579279fba7436d58a1c49288909.ttf create mode 100644 _static/fonts/765bd4a97597a4d7781193793477a6cd.ttf create mode 100644 _static/fonts/76945c7494c20515bb45d1dedab8f706.woff2 create mode 100644 _static/fonts/76da333ab59c6d625cabfb0768f82b4a.woff2 create mode 100644 _static/fonts/770518db51bed1e082feecc532cfcbf8.woff2 create mode 100644 _static/fonts/77b24796a3d4ab521f66765651875338.woff2 create mode 100644 _static/fonts/77ff81100e5a1db3d925f713660700ad.woff2 create mode 100644 _static/fonts/78a9265759e7b861a1639a36f4c01d04.woff2 create mode 100644 _static/fonts/7af61b2367eba2b1852e837c46a75696.woff2 create mode 100644 _static/fonts/7b63598dcc2a26583b82594bd0e36d5b.woff2 create mode 100644 _static/fonts/7b8c2179b6b778308d2ff39bdb82e926.woff2 create mode 100644 _static/fonts/7e262106f82cc52663e403f5b73795bb.woff2 create mode 100644 _static/fonts/7f1c829b0c90fd664a03bb714a74f7d3.woff2 create mode 100644 _static/fonts/7fa86b886bee5d6ab420a8e89b9f3052.ttf create mode 100644 _static/fonts/8007dfe835cfb201b8caaa9651098588.woff2 create mode 100644 _static/fonts/83614c36460a4a9734968789cb535de7.woff2 create mode 100644 _static/fonts/84e959dd07f302392f0ffd86f87db888.ttf create mode 100644 _static/fonts/85a41b80c5fdc14e3dc48636a30d87dd.woff2 create mode 100644 _static/fonts/870e5928dd14fcfe0ce9386107666774.woff2 create mode 100644 _static/fonts/8898c4b754d5d96c1a5e1b1d54100554.woff2 create mode 100644 _static/fonts/89b4f174a5a728d2d8c85b87990c9ab4.ttf create mode 100644 _static/fonts/8a8dca39f24b52e89e6fd6dcd8b6dd32.woff2 create mode 100644 _static/fonts/8aa562790559d61dd5178a88a296d70f.ttf create mode 100644 _static/fonts/8c3798e37724f71bc0c63c44a5307413.woff2 create mode 100644 _static/fonts/8c49ed8b472d38d3985ec9bbbccea601.ttf create mode 100644 _static/fonts/8e48cf20cf9f9e5feb7197c79028132b.woff2 create mode 100644 _static/fonts/9095d663e4d450059bcc2260bb75cd62.woff2 create mode 100644 _static/fonts/90ebb29b5cffa197b184773983ba7e91.woff2 create mode 100644 _static/fonts/93b6c99d936df38895a0d95e3ffea2fd.woff2 create mode 100644 _static/fonts/9582ced8a675bf267cc7ac392a86413e.woff2 create mode 100644 _static/fonts/99be4d68845d66c27c7f7d3a48687b66.woff2 create mode 100644 _static/fonts/99cf36e763be9cce7b4c59b91841af58.woff2 create mode 100644 _static/fonts/9a9bf2d91ebbb1b96eab8eb0b0514bcc.woff2 create mode 100644 _static/fonts/9bcbc88b33b2efc2aee821b831499f1c.woff2 create mode 100644 _static/fonts/9c9be791a58af8a04c611ca1d13f51c6.woff2 create mode 100644 _static/fonts/9fdb12ceee3a402d3a54afe354552459.woff2 create mode 100644 _static/fonts/a6933e678530b263486fa7b185a449ca.woff2 create mode 100644 _static/fonts/a6caf7b9888eb0c382948c1ca5e8bebb.woff2 create mode 100644 _static/fonts/a70ff2592da5e3453943f727633aff54.woff2 create mode 100644 _static/fonts/aa28d99c7db60ad23f96a5c317615c42.woff2 create mode 100644 _static/fonts/aab05142e0e2dadf7df633e061e612ad.woff2 create mode 100644 _static/fonts/ab03beb9091fa15ce4e783199e076bc6.woff2 create mode 100644 _static/fonts/ac848474638236e67a64bc654fb18de0.ttf create mode 100644 _static/fonts/acaac043ca238f0e56e61864456777fa.woff2 create mode 100644 _static/fonts/aeed0e51b0bac7c89e5c7e6cf086d7e0.woff2 create mode 100644 _static/fonts/b019538234514166ec7665359d097403.woff2 create mode 100644 _static/fonts/b076e86301cbee8c5c9aef51863a9c0a.woff2 create mode 100644 _static/fonts/b19ac4e57f2a56639eebd1c35319e5a7.woff2 create mode 100644 _static/fonts/b4d3c40a77fd9e35a881a79077957055.woff2 create mode 100644 _static/fonts/b4e42731e8d667ae87c3450c345754ae.woff2 create mode 100644 _static/fonts/b57a5ada789f195d5d42f4073a6cf313.woff2 create mode 100644 _static/fonts/b5b4146d87e5d22d0a4e0d04f3ee5626.woff2 create mode 100644 _static/fonts/b7ef2cd1159a8cbfd271ff2abe07f237.woff2 create mode 100644 _static/fonts/b93199bb6f964f190f4da04ecdbaf5a4.woff2 create mode 100644 _static/fonts/bb8007225d94a099cddbade7ea904667.woff2 create mode 100644 _static/fonts/bc67bba106323289ea3eda0826de1912.ttf create mode 100644 _static/fonts/bcd47c2f3649cfcaa86a08fb741255d6.woff2 create mode 100644 _static/fonts/bd0efe13f0d9d591b337ddc7f289f494.woff2 create mode 100644 _static/fonts/bd51fb0ca67e64c809ffcf7e1370f969.woff2 create mode 100644 _static/fonts/bdbb6b52604c2451fdcba9cdfd44f4e1.woff2 create mode 100644 _static/fonts/bf2ad3287f13eb7076cccb516ec2986f.ttf create mode 100644 _static/fonts/bfd1a0c9c783e84595589f33e1828a57.woff2 create mode 100644 _static/fonts/c13b34dd5b6a35b309944b61c91b2ace.woff2 create mode 100644 _static/fonts/c22066c14662d6c80415ae04c5dd9d51.woff2 create mode 100644 _static/fonts/c28a41f656599f6694528b5463c6a445.woff2 create mode 100644 _static/fonts/c6dc61b627bbc5af9130518297bd4f17.ttf create mode 100644 _static/fonts/c8a9fd4eab4e83382cc66fde70911b41.woff2 create mode 100644 _static/fonts/ca7eea0cf248d6e8442c01074765bd33.woff2 create mode 100644 _static/fonts/cadfb311297a9362b07fab73934b432a.ttf create mode 100644 _static/fonts/cbfd26d5bcf084ee407a0b2b7599e84b.woff2 create mode 100644 _static/fonts/ccdebed88064e470c15f37c432922e57.woff2 create mode 100644 _static/fonts/cce2217cc8323fe49789adefb3596291.woff2 create mode 100644 _static/fonts/cd3d1f17e048e2116f438bd7157baccf.woff2 create mode 100644 _static/fonts/d07f561ba87d93460742b060727d9e0d.woff2 create mode 100644 _static/fonts/d368cf5bed7856dbafa2af36b51acb9c.woff2 create mode 100644 _static/fonts/d422317033deb87342a5e56c7be67458.ttf create mode 100644 _static/fonts/d6f9cdf1a40893111566fcdee3bbe5a9.woff2 create mode 100644 _static/fonts/d98f35e926c11f3d5c0c8e3205d43907.ttf create mode 100644 _static/fonts/d9e6a498dac7e9e91f6e0b4f8930eba0.woff2 create mode 100644 _static/fonts/da6cd48e6dad1888fccc91735e7522f7.woff2 create mode 100644 _static/fonts/daf12b5f1889502004bba85ad71f9fa4.woff2 create mode 100644 _static/fonts/daf51ab540602b2d0b87646621637bac.woff2 create mode 100644 _static/fonts/db0424fb67fb52e7e538490240cc7fb9.woff2 create mode 100644 _static/fonts/dc25cbf4baaf778bd8ae78fbc0e79479.woff2 create mode 100644 _static/fonts/dd719f1662079ce6a61260f9af972379.woff2 create mode 100644 _static/fonts/de018865c95896bb57265fc97c48ebd7.woff2 create mode 100644 _static/fonts/e33716333704ab19fdf9989e072ad49a.woff2 create mode 100644 _static/fonts/e56cc9fb5272752b78f144b4be43175d.woff2 create mode 100644 _static/fonts/e704ef18719c08839bc99a32437ef0f8.woff2 create mode 100644 _static/fonts/e99627cd27de169d23ece4573006af2a.woff2 create mode 100644 _static/fonts/ef8f0236a7e8b46bc9d642ecf4ab0cb7.woff2 create mode 100644 _static/fonts/f154d62b4879af7a22895af7a4ef03f0.woff2 create mode 100644 _static/fonts/f17ee050ada0453f3bd07bc466c2dde2.woff2 create mode 100644 _static/fonts/f265cee675c0e5b2d6ab263d0edcc754.woff2 create mode 100644 _static/fonts/f2f69e8cd15fdd15a4244c95ec8a8514.woff2 create mode 100644 _static/fonts/f534242dea2255c25b9d05c2371986e3.woff2 create mode 100644 _static/fonts/f53f3b5a15d717b6d21d7885285e90ed.woff2 create mode 100644 _static/fonts/f55dac651a40fce74a5cf5728d9f8ffc.woff2 create mode 100644 _static/fonts/f5aebdfea35d1e7656ef4acc5db1f243.woff2 create mode 100644 _static/fonts/f5f971e9640a9eb86ef553a7e7e999c7.woff2 create mode 100644 _static/fonts/f6734f8177112c0839b961f96d813fcb.woff2 create mode 100644 _static/fonts/f75911313e1c7802c23345ab57e754d8.woff2 create mode 100644 _static/fonts/fb17f56622e45dd4ecee00bb5c63cd2b.woff2 create mode 100644 _static/fonts/fb1aaa90783b8cb9375265abeb91b153.woff2 create mode 100644 _static/fonts/fc66f942651a9fe1a598770d3d896529.woff2 create mode 100644 _static/language_data.js create mode 100644 _static/sphinx_immaterial_theme.1b5b7a2d5891aec19.min.js create mode 100644 _static/sphinx_immaterial_theme.af531f03affe68837.min.css create mode 100644 _static/white.svg create mode 100644 api.html create mode 100644 genindex.html create mode 100644 http-routingtable.html create mode 100644 index.html create mode 100644 introduction.html create mode 100644 objects.inv create mode 100644 rdfm_android_device_client.html create mode 100644 rdfm_artifact.html create mode 100644 rdfm_linux_device_client.html create mode 100644 rdfm_manager.html create mode 100644 rdfm_mgmt_server.html create mode 100644 rdfm_ota_manual.html create mode 100644 searchindex.js create mode 100644 server_operation.html create mode 100644 system_overview.html diff --git a/.buildinfo b/.buildinfo new file mode 100644 index 0000000..7de3e43 --- /dev/null +++ b/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 7c1cccbc9b07fcc5ab0a2891b3ff1abf +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/_images/summary.png b/_images/summary.png new file mode 100644 index 0000000000000000000000000000000000000000..3057b048a30370ead374780738abecda19cbed24 GIT binary patch literal 33388 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 literal 0 HcmV?d00001 diff --git a/_sources/api.rst.txt b/_sources/api.rst.txt new file mode 100644 index 0000000..517d0fa --- /dev/null +++ b/_sources/api.rst.txt @@ -0,0 +1,82 @@ + +RDFM Server API Reference +------------------------- + +API Authentication +~~~~~~~~~~~~~~~~~~ + +By default, the RDFM server expects all API requests to be authenticated. +Depending on the type of the API, this can be either: + +* Device Token +* Management Token + +In either case, the server expects the token to be passed as part of the request, in the HTTP Authorization header. +An example authenticated request is shown below: + +.. sourcecode:: http + + GET /api/v1/groups HTTP/1.1 + Host: rdfm-server:5000 + User-Agent: python-requests/2.31.0 + Accept-Encoding: gzip, deflate + Accept: */* + Connection: keep-alive + Authorization: Bearer token=eyJhbGciOiJSUzI1NiIsInR5cC<...truncated...>RpPonb7-IAsk89YpGayxg + +Any request that was not successfully authenticated (because of a missing or otherwise invalid token) will return the 401 Unauthorized status code. +Additionally, in the case of management tokens, if the given token does not provide sufficient access to the requested resource, the request will be rejected with a 403 Forbidden status code. +This can happen if the token does not claim all scopes required by the target endpoint (for example: trying to upload a package using a read-only token). + +Error Handling +~~~~~~~~~~~~~~ + +Should an error occur during the handling of an API request, either because of incorrect request data or other endpoint-specific scenarios, the server will return an error structure containing a user-friendly description of the error. +An example error response is shown below: + +.. sourcecode:: json + + { + "error": "delete failed, the package is assigned to at least one group" + } + + +Packages API +~~~~~~~~~~~~ + +.. autoflask:: rdfm_mgmt_server:app + :modules: api.v1.packages + :undoc-static: + :order: path + +Group API +~~~~~~~~~ + +.. autoflask:: rdfm_mgmt_server:app + :modules: api.v1.groups + :undoc-static: + :order: path + +Update API +~~~~~~~~~~ + +.. autoflask:: rdfm_mgmt_server:app + :modules: api.v1.update + :undoc-static: + :order: path + +Device Management API +~~~~~~~~~~~~~~~~~~~~~ + +.. autoflask:: rdfm_mgmt_server:app + :modules: api.v1.devices + :undoc-static: + :order: path + +Device Authorization API +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoflask:: rdfm_mgmt_server:app + :modules: api.v1.auth + :undoc-static: + :order: path diff --git a/_sources/index.md.txt b/_sources/index.md.txt new file mode 100644 index 0000000..bb0601f --- /dev/null +++ b/_sources/index.md.txt @@ -0,0 +1,16 @@ +# {{project}} + +```{toctree} +:maxdepth: 2 + +introduction +system_overview +rdfm_linux_device_client +rdfm_android_device_client +rdfm_artifact +rdfm_manager +rdfm_mgmt_server +rdfm_ota_manual +server_operation +api +``` diff --git a/_sources/introduction.md.txt b/_sources/introduction.md.txt new file mode 100644 index 0000000..faa508e --- /dev/null +++ b/_sources/introduction.md.txt @@ -0,0 +1,14 @@ +# Introduction + +RDFM - Remote Device Fleet Manager - is an open-source ecosystem of tools that enable Over-The-Air (OTA) update delivery and fleet management for systems of embedded devices. + +This manual describes the main components of RDFM. It is divided into the following chapters: + +- System Architecture - a short overview of the system architecture, and how each component of the system interacts with the other +- RDFM Linux Device Client - build instructions and manual for the Linux RDFM Client, used for installing updates on a device +- RDFM Android Device Client - integration guide and user manual for the RDFM Android Client/app used for providing OTA updates via RDFM on embedded Android devices +- RDFM Artifact utility - instruction manual for the `rdfm-artifact` utility used for generating update packages for use with the RDFM Linux device client +- RDFM Manager utility - instruction manual for the `rdfm-mgmt` utility, which allows management of devices connected to the RDFM server +- RDFM Management Server - build instructions and deployment manual for the RDFM Management Server +- RDFM Server API Reference - comprehensive reference of the HTTP APIs exposed by the RDFM Management Server +- RDFM OTA Manual - introduces key concepts of the RDFM OTA system and explains it's basic operation principles \ No newline at end of file diff --git a/_sources/rdfm_android_device_client.md.txt b/_sources/rdfm_android_device_client.md.txt new file mode 100644 index 0000000..4e30b17 --- /dev/null +++ b/_sources/rdfm_android_device_client.md.txt @@ -0,0 +1,121 @@ +# RDFM Android Device Client + +## Introduction + +The RDFM Android Device Client allows for integrating an Android-based device with the RDFM server. +Currently, only OTA update functionality is implemented. + +## Integrating the app + +This app is **not meant to be built separately** (i.e in Android Studio), but as part of the source tree for an existing device. +The app integrates with the Android UpdateEngine to perform the actual update installation, which requires it to be a system app. +Some configuration is required to the existing system sources. + +### Copying the sources + +First, copy the sources of the app to the root directory of the AOSP source tree. +After cloning this repository, run the following: +``` +mkdir -v -p /vendor/antmicro/rdfm +cd devices/android-client/ +cp -r app/src/main/* +``` + +### Configuring the device Makefile + +The [product Makefile](https://source.android.com/docs/setup/create/new-device#build-a-product) must be configured to build the RDFM app into the system image. +To do this, add `rdfm` to the `PRODUCT_PACKAGES` variable in the target device Makefile: +``` +PRODUCT_PACKAGES += rdfm +``` + +### Building the app + +Afterwards, [the usual Android build procedure](https://source.android.com/docs/setup/build/building) can be used to build just the app. +From an already configured build environment, run: +``` +mma rdfm +``` +The resulting signed APK is in `out/target/product//system/app/rdfm/rdfm.apk`. + +## System versioning + +The app performs update check requests to the configured RDFM server. +The build version and device type are retrieved from the system properties: +- `ro.build.version.incremental` - the current software version (matches `rdfm.software.version`) +- `ro.build.product` - device type (matches `rdfm.hardware.devtype`) + +When uploading an OTA package to the RDFM server, currently these values must be **manually** extracted from the update package, and passed as arguments to `rdfm-mgmt`: +``` +rdfm-mgmt packages upload --path ota.zip --version --device +``` + +You can extract the values from the [package metadata file](https://source.android.com/docs/core/ota/tools#ota-package-metadata) by unzipping the OTA package. + +## Configuring the app + +The application will automatically start on system boot. +Available configuration options are shown below. + +### Build-time app configuration + +The default build-time configuration can be modified by providing a custom `conf.xml` file in the `app/src/main/res/values/` folder, similar to the one shown below: + +```xml + + + + +``` + +This build-time configuration is applied **only once, at first startup of the app**, as the main use case for this is first-time configuration for newly provisioned devices. +Modifying it afterwards (for example, via an update containing a new version of the RDFM app) will not result in the change of existing configuration. + +### Runtime app configuration + +It is possible to change the app's configuration at runtime by simply starting the RDFM app from the drawer and selecting `Settings` from the context menu. + +### Update check interval + +Currently, the app has a hardcoded update check interval of 4 minutes. +This will be extended in the future to allow configuring it in a similar way as shown above in the server address case. + +## Development + +The provided Gradle files can be used for development purposes, simply open the `devices/android-client` directory in Android Studio. +Missing references to the `UpdateEngine` class are expected, but they do not prevent regular use of the IDE. + +Do note however that **the app is not buildable from Android Studio**, as it requires integration with the aforementioned system API. +To test the app, an existing system source tree must be used. +Copy the modified sources to the AOSP tree, and re-run the [application build](#building-the-app). +The modified APK can then be uploaded to the device via ADB by running: +``` +adb install +``` + +### Restarting the app + +With the target device connected via ADB, run: +``` +adb shell am force-stop com.antmicro.update.rdfm +adb shell am start -n com.antmicro.update.rdfm/.MainActivity +``` + +### Fetching app logs + +To view the application logs, run: +``` +adb logcat --pid=`adb shell pidof -s com.antmicro.update.rdfm` +``` diff --git a/_sources/rdfm_artifact.md.txt b/_sources/rdfm_artifact.md.txt new file mode 100644 index 0000000..4fea9ef --- /dev/null +++ b/_sources/rdfm_artifact.md.txt @@ -0,0 +1,85 @@ +# RDFM Artifact utility + +## Introduction + +The RDFM Artifact tool (`rdfm-artifact`) allows for easy creation and modification of RDFM Linux client-compatible artifacts containing rootfs partition images. +A basic RDFM artifact consists of a rootfs image, as well as its checksum, metadata and compatibility with certain device types. + +Additionally, `rdfm-artifact` allows for the generation of delta updates, which contain only the differences between two versions of an artifact rather than the entire artifact itself. +This can be useful for reducing the size of updates and improving the efficiency of the deployment process. + +## Getting started + +In order to support robust updates and rollback, the RDFM Client requires proper partition layout and integration with the U-Boot bootloader. To make it easy to integrate the RDFM Client into your Yocto image-building project, it's recommended to use the [meta-rdfm](https://github.com/antmicro/meta-antmicro/tree/master/meta-rdfm) Yocto layer when building the BSPs. + +## Building from source + +### Requirements + +* Go compiler +* C Compiler +* liblzma-dev and libglib2.0-dev packages + +### Steps + +To build `rdfm-artifact` on a device from source, clone the repository and build the binary using `make`: + +``` +git clone https://github.com/antmicro/rdfm.git && cd tools/rdfm-artifact/ +make +``` + +## Basic usage + +The basic functionality of writing an artifact is available with the `write` subcommand: + +``` +NAME: + rdfm-artifact write - Allows creation of RDFM-compatible artifacts + +USAGE: + rdfm-artifact write command [command options] [arguments...] + +COMMANDS: + rootfs-image Create a full rootfs image artifact + delta-rootfs-image Create a delta rootfs artifact + +OPTIONS: + --help, -h show help +``` + +### Creating a full-rootfs artifact + +For example, to create a simple rootfs artifact for a given system image: + +``` +rdfm-artifact write rootfs-image \ + --file "my-rootfs-image.img" \ + --artifact-name "my-artifact-name" \ + --device-type "my-device-type" \ + --output-path "path-to-output.rdfm" +``` + +### Creating a delta rootfs artifact + +For creating a delta artifact, you should have already created two separate full-rootfs artifacts: + +- base artifact - the rootfs image that the deltas will be applied on top of, or in other words: the currently running rootfs on the device +- target artifact - the updated rootfs image that will be installed on the device + +Given these two artifacts, a delta artifact can be generated like this: + +``` +rdfm-artifact write delta-rootfs-image \ + --base-artifact "base.rdfm" \ + --target-artifact "target.rdfm" \ + --output-path "base-to-target.rdfm" +``` + +## Running tests + +To run `rdfm-artifact` tests, use the `test` Makefile target: + +``` +make test +``` diff --git a/_sources/rdfm_linux_device_client.md.txt b/_sources/rdfm_linux_device_client.md.txt new file mode 100644 index 0000000..8833c10 --- /dev/null +++ b/_sources/rdfm_linux_device_client.md.txt @@ -0,0 +1,68 @@ +# RDFM Linux Device Client + +## Introduction + +The RDFM Linux Device Client (`rdfm-client`) integrates an embedded Linux device with the RDFM Server. +This allows for performing robust Over-The-Air (OTA) updates of the running system and remote management of the device. + +`rdfm-client` runs on the target Linux device and handles the process of checking for updates in the background along with maintaining a connection to the RDFM Management Server. + +## Getting started + +In order to support robust updates and rollback, the RDFM Client requires proper partition layout and integration with the U-Boot bootloader. To make it easy to integrate the RDFM Client into your Yocto image-building project, it's recommended to use the [meta-rdfm](https://github.com/antmicro/meta-antmicro/tree/master/meta-rdfm) Yocto layer when building the BSPs. + +## Installing from source + +### Requirements + +* C compiler +* Go compiler +* liblzma-dev, libssl-dev and libglib2.0-dev packages + +### Steps + +To install an RDFM client on a device from source, first clone the repository and build the binary: +``` +git clone https://github.com/antmicro/rdfm.git && cd devices/linux-client/ +make +``` + +Then run the install command: +``` +make install +``` + +### Installation notes + +Installing `rdfm` this way does not offer a complete system updater. +System updates require additional integration with the platform's bootloader and a dual-root partition setup for robust updates. +For this, it's recommended to build complete BSPs containing `rdfm` using the [meta-rdfm](https://github.com/antmicro/meta-antmicro/tree/master/meta-rdfm) Yocto layer. + +## Building using Docker + +All build dependencies for compiling the RDFM Client are included in a dedicated Dockerfile. To build a development container image, you can use: + +``` +git clone https://github.com/antmicro/rdfm.git && cd devices/linux-client/ +sudo docker build -t rdfmbuilder . +``` + +This will create a Docker image that can be later used to compile the RDFM binary: + +``` +sudo docker run --rm -v :/data -it rdfmbuilder +cd data/devices/linux-client +make +``` + +## Developer Guide + +### Running tests + +Use the `test` make target to run the unit tests: + +``` +make test +``` + +Additionally, run the scripts available in the `scripts/test-docker` directory. These scripts test basic functionality of the RDFM client. diff --git a/_sources/rdfm_manager.md.txt b/_sources/rdfm_manager.md.txt new file mode 100644 index 0000000..f2663b7 --- /dev/null +++ b/_sources/rdfm_manager.md.txt @@ -0,0 +1,201 @@ +# RDFM Manager utility + +## Introduction + +The RDFM Manager (`rdfm-mgmt`) utility allows authorized users to manage resources exposed by the RDFM Management Server. + +## Installation + +Before proceeding, make sure that you have installed Python (at least version 3.11) and the `pipx` utility: +- **Debian (Bookworm)** - run `sudo apt update && sudo apt install pipx` +- **Arch** - `sudo pacman -S python-pipx` + +The prefered mode of installation for `rdfm-mgmt` is via `pipx`. +To install `rdfm-mgmt`, you must first clone the RDFM repository: + +``` +git clone https://github.com/antmicro/rdfm.git +cd rdfm/ +``` + +Afterwards, run the following commands: + +``` +cd manager/ +pipx install . +``` + +This will install the `rdfm-mgmt` utility and its dependencies for the current user within a virtual environment located at `/home//.local/pipx/venv`. +The `rdfm-mgmt` executable will be placed in `/home//.local/bin/` and should be immediately accessible from the shell. +Depending on the current system configuration, adding the above directory to the `PATH` may be required. + +## Configuration + +Additional RDFM Manager configuration is stored in the current user's `$HOME` directory, in the `$HOME/.config/rdfm-mgmt/config.json` file. +By default, RDFM Manager will add authentication data to all requests made to the RDFM server, which requires configuration of an authorization server and client credentials for use with the OAuth2 `Client Credentials` flow. +If authentication was disabled on the server-side, you can disable it in the manager as well by passing the `--no-api-auth` CLI flag like so: + +``` +rdfm-mgmt --no-api-auth groups list +``` + +An example configuration file is shown below. +In this case, the [Keycloak authorization server](https://www.keycloak.org/) was used: + +```json +{ + "auth_url": "http://keycloak:8080/realms/master/protocol/openid-connect/token", + "client_id": "rdfm-client", + "client_secret": "RDSwDyUMOT7UXxMqMmq2Y4vQ1ezxqobi" +} +``` + +Explanation of each required configuration field is shown below: +- `auth_url` - URL to the authorization server's [token endpoint](https://swagger.io/docs/specification/authentication/openid-connect-discovery/) +- `client_id` - Client ID to use for authentication using OAuth2 Client Credentials flow +- `client_secret` - Client secret to use for authentication using OAuth2 Client Credentials flow + +:::{note} +If you're also setting up the server, please note that the above client credentials are **NOT** the same as the server's Token Introspection credentials. +Each user of ``rdfm-mgmt`` should receive different credentials and be assigned scopes based on their allowed access level. +::: + + +## Building the wheel + +For installation instructions, see the [Installation section](#installation). +Building the wheel is not required in this case. + +To build the `rdfm-mgmt` wheel, you must have Python 3 installed, along with the `Poetry` dependency manager. + +Building the wheel can be done as follows: + +``` +cd manager/ +poetry build +``` + +## Usage + +For more detailed information, see the help messages associated with each subcommand: + +``` +$ rdfm-mgmt -h +usage: rdfm-mgmt + +RDFM Manager utility + +options: + -h, --help show this help message and exit + --url URL URL to the RDFM Management Server (default: http://127.0.0.1:5000/) + --cert CERT path to the server CA certificate used for establishing an HTTPS connection (default: ./certs/CA.crt) + --no-api-auth disable OAuth2 authentication for API requests (default: False) + +available commands: + {devices,packages,groups} + devices device management + packages package management + groups group management +``` + +### Listing available resources + +Listing devices: + +``` +rdfm-mgmt devices list +``` + +Listing registration requests: + +``` +rdfm-mgmt devices pending +``` + + +Listing packages: + +``` +rdfm-mgmt packages list +``` + +Listing groups: + +``` +rdfm-mgmt groups list +``` + +### Uploading packages + +``` +rdfm-mgmt packages upload \ + --path file.img \ + --version "v0" \ + --device "x86_64" +``` + +### Deleting packages + +``` +rdfm-mgmt packages delete --package-id +``` + +### Creating groups + +``` +rdfm-mgmt groups create --name "Group #1" --description "A very long description of the group" +``` + +### Deleting groups + +``` +rdfm-mgmt groups delete --group-id +``` + +### Assign package to a group + +Assigning one package: + +``` +rdfm-mgmt groups assign-package --group-id --package-id +``` + +Assigning many packages: + +``` +rdfm-mgmt groups assign-package --group-id --package-id --package-id +``` + +Clearing package assignments: + +``` +rdfm-mgmt groups assign-package --group-id +``` + +### Assign devices to a group + +Adding devices: + +``` +rdfm-mgmt groups modify-devices --group-id --add +``` + +Removing devices: + +``` +rdfm-mgmt groups modify-devices --group-id --remove +``` + +### Setting a group's target version + +``` +rdfm-mgmt groups target-version --group-id --version +``` + +### Authorizing a device + +``` +rdfm-mgmt devices auth +``` + +You can then select the registration for this device to authorize. diff --git a/_sources/rdfm_mgmt_server.md.txt b/_sources/rdfm_mgmt_server.md.txt new file mode 100644 index 0000000..8406266 --- /dev/null +++ b/_sources/rdfm_mgmt_server.md.txt @@ -0,0 +1,343 @@ +# RDFM Management Server + +## Introduction + +The RDFM Management Server is a core part of the RDFM ecosystem. The server manages incoming device connections and grants authorization only to those which are allowed to check-in with the server. +It also handles package upload and management, deploy group management and other crucial functionality required for robust and secure device Over-The-Air (OTA) updates along with allowing remote system management without exposing devices to the outside world. + +## Database backend + +Currently, to simplify development, the RDFM Management Server utilizes SQLite for storing server data. The database is stored in the `devices.db` file. This will be expanded in the future to allow using the typical DBMS backends instead. + +## REST API + +The server exposes a management and device API that is used by management software and end devices. A comprehensive list of all API endpoints is available in the [RDFM Server API Reference chapter](api.rst). + +## Building + +Note: It is recommended to use the [Dockerized development environment](#setting-up-a-dockerized-development-environment), to ensure the correct Python version is installed. The server requires a relatively modern version of Python, which may not be readily available on all distributions. + +To build the server, you must have Python 3.11 installed, along with the `Poetry` dependency manager. + +First, clone the RDFM repository: + +``` +git clone https://github.com/antmicro/rdfm.git +cd rdfm/ +``` + +Next, building the wheel can be done as follows: + +``` +cd server/ +poetry build +``` + +## Setting up a development environment + +This section pertains only to the development setup. **This should not be used for production deployments!** +For this section, it is assumed that you have cloned the RDFM repository already. + +The server utilizes the Poetry tool to manage project dependencies. +You can run a development RDFM Management Server by running the following commands: + +```bash +cd server/ +export JWT_SECRET="THISISATESTDEVELOPMENTSECRET123" +poetry build && poetry install && poetry run python -m rdfm_mgmt_server --no-ssl --no-api-auth --local-package-dir ./packages/ +``` + +This launches the RDFM Management Server with no encryption, listening on `localhost`/`127.0.0.1`. +By default, the HTTP API is exposed on port `5000`. + +When server is in debug mode (`app.run(debug=True, ...)` is set) every HTTP request received and response to it are printed to STDOUT. + +## Setting up a Dockerized development environment + +For this section, it is assumed that you have cloned the RDFM repository already. + +The RDFM server can also be deployed using a Docker container. +A `Dockerfile` is provided in the `server/deploy/` directory that builds a container suitable for running the server. +To manually build the container image, run the following from the **RDFM repository root** folder: + +```bash +docker build -f server/deploy/Dockerfile -t antmicro/rdfm-server:latest . +``` + +A simple `docker-compose` file that can be used to run the server is provided below, and in the `server/deploy/docker-compose.development.yml` file. + +```yaml +services: + rdfm-server: + image: antmicro/rdfm-server:latest + restart: unless-stopped + environment: + - RDFM_JWT_SECRET= + - RDFM_DB_CONNSTRING=sqlite:////database/development.db + - RDFM_HOSTNAME=rdfm-server + - RDFM_API_PORT=5000 + - RDFM_DISABLE_ENCRYPTION=1 + - RDFM_DISABLE_API_AUTH=1 + - RDFM_LOCAL_PACKAGE_DIR=/packages/ + ports: + - "5000:5000" + volumes: + - db:/database/ + - pkgs:/packages/ + +volumes: + db: + pkgs: +``` + +The server can then be started using the following command: + +```bash +docker-compose -f server/deploy/docker-compose.development.yml up +``` + +Configuration of the RDFM server can be changed by using the following environment variables: + +- `RDFM_JWT_SECRET` - secret key used by the server when issuing JWT tokens, this value must be kept secret and not easily guessable (for example, a random hexadecimal string). +- `RDFM_HOSTNAME` - hostname/IP address to listen on, when running the server in a container use the service name here (using above example, `rdfm-server`). +- `RDFM_API_PORT` - HTTP API port +- `RDFM_DB_CONNSTRING` - database connection string, for examples please refer to: [SQLAlchemy - Backend-specific URLs](https://docs.sqlalchemy.org/en/20/core/engines.html#backend-specific-urls). Currently, only the SQLite and PostgreSQL engines were verified to work with RDFM (however: the PostgreSQL engine requires adding additional dependencies which are currently not part of the default server image, this may change in the future). +- `RDFM_DISABLE_ENCRYPTION` - disables encryption of device-server protocol data and usage of HTTPS in the API routes +- `RDFM_SERVER_CERT` - when using encryption, path to the server's certificate. The certificate can be stored on a Docker volume mounted to the container. For reference on generating the certificate/key pairs, see the `server/tests/certgen.sh` script. +- `RDFM_SERVER_KEY` - when using encryption, path to the server's private key. Additionally, the above also applies here. +- `RDFM_LOCAL_PACKAGE_DIR` - specifies a path (local for the server) to a directory where the packages are stored +- `RDFM_STORAGE_DRIVER` - storage driver to use for storing artifacts. Accepted values: `local` (default), `s3`. +- `RDFM_DISABLE_API_AUTH` - disables request authentication on the exposed API routes. **WARNING: This is a development flag only! Do not use in production!** This causes all API methods to be freely accessible, without any access control in place! +- `RDFM_OAUTH_URL` - specifies the URL to an authorization server endpoint compatible with the RFC 7662 OAuth2 Token Introspection extension. This endpoint is used to authorize access to the RDFM server based on tokens provided in requests made by API users. +- `RDFM_OAUTH_CLIENT_ID` - if the authorization server endpoint provided in `RDFM_OAUTH_URL` requires the RDFM server to authenticate, this variable defines the OAuth2 `client_id` used for authentication. +- `RDFM_OAUTH_CLIENT_SEC` - if the authorization server endpoint provided in `RDFM_OAUTH_URL` requires the RDFM server to authenticate, this variable defines the OAuth2 `client_secret` used for authentication. + +## Configuring package storage location + +### Storing packages locally + +By default (when not using one of the above deployment setups), the server stores all uploaded packages to a temporary folder under `/tmp/.rdfm-local-storage/`. +To persist package data, configuration of an upload folder is required. +This can be done by using the `RDFM_LOCAL_PACKAGE_DIR` environment variable (in the Dockerized deployment), which should contain a path to the desired upload folder. + +:::{warning} +This storage method should NOT be used for production deployments! +The performance of the built-in file server is severely limited and provides NO caching, which will negatively affect the update speed for all devices even when a few of them try downloading an update package at the same time. +It is recommended to use a dedicated storage solution such as S3 to store packages. +::: + +### Storing packages on S3-compatible storage + +The RDFM server can also store package data on S3 and other S3 API-compatible object storage servers. +The following environment variables allow changing the configuration of the S3 integration: +- `RDFM_S3_BUCKET` - name of the bucket to upload the packages to +- `RDFM_S3_ACCESS_KEY_ID` - Access Key ID to access the specified bucket +- `RDFM_S3_ACCESS_SECRET_KEY` - Secret Access Key to access the specified bucket +Additionally, when using S3 storage, the environment variable `RDFM_STORAGE_DRIVER` must be set to `s3`. + +An example reference setup utilizing the MinIO Object Storage server is provided in the `server/deploy/docker-compose.minio.yml` file. +To run it, first build the RDFM server container like in the above setup guides: + +```bash +docker build -f server/deploy/Dockerfile -t antmicro/rdfm-server:latest . +``` + +Then, run the following: + +``` +docker-compose -f server/deploy/docker-compose.minio.yml up +``` + +## Configuring API authentication + +### Basic configuration + +The above development setup does not provide any authentication for the RDFM API. +This is helpful for development or debugging purposes, however **under no circumstance should this be used in production deployments, as it exposes the entire API with no restrictions in place**. + +By default, the RDFM server requires configuration of an external authorization server to handle token creation and scope management. +To be compatible with RDFM Management Server, the authentication server **MUST** support the OAuth2 Token Introspection extension ([RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662)). + +The authorization server is configured using the following environment variables: +- `RDFM_OAUTH_URL` - specifies the URL to the Token Introspection endpoint of the authorization server. +- `RDFM_OAUTH_CLIENT_ID` - specifies the client identifier to use for authenticating the RDFM server to the authorization server. +- `RDFM_OAUTH_CLIENT_SEC` - specifies the client secret to use for authenticating the RDFM server to the authorization server. + +For accessing the management API, the RDFM server does not issue any tokens itself. +This task is delegated to the authorization server that is used in conjunction with RDFM. +The following scopes are used for controlling access to different methods of the RDFM API: +- `rdfm_admin_ro` - read-only access to the API (fetching devices, groups, packages) +- `rdfm_admin_rw` - complete administrative access to the API with modification rights + +Refer to the [RDFM Server API Reference chapter](api.rst) for a breakdown of the scopes required for accessing each API method. + +### API authentication using Keycloak + +#### Running the services + +An example `docker-compose` file that can be used to run the RDFM server using [Keycloak Identity and Access Management server](https://www.keycloak.org/) as an authorization server is provided below, and in the `server/deploy/docker-compose.keycloak.development.yml` file. + +```yaml +services: + rdfm-server: + image: antmicro/rdfm-server:latest + restart: unless-stopped + environment: + - RDFM_JWT_SECRET= + - RDFM_DB_CONNSTRING=sqlite:////database/development.db + - RDFM_HOSTNAME=rdfm-server + - RDFM_API_PORT=5000 + - RDFM_DISABLE_ENCRYPTION=1 + - RDFM_LOCAL_PACKAGE_DIR=/packages/ + - RDFM_OAUTH_URL=http://keycloak:8080/realms/master/protocol/openid-connect/token/introspect + - RDFM_OAUTH_CLIENT_ID=rdfm-server-introspection + - RDFM_OAUTH_CLIENT_SEC= + networks: + - rdfm + ports: + - "5000:5000" + volumes: + - db:/database/ + - pkgs:/packages/ + + keycloak: + image: quay.io/keycloak/keycloak:22.0.1 + restart: unless-stopped + environment: + - KEYCLOAK_ADMIN=admin + - KEYCLOAK_ADMIN_PASSWORD=admin + networks: + - rdfm + ports: + - "8080:8080" + command: + - start-dev + volumes: + - keycloak:/opt/keycloak/data/ + +volumes: + db: + pkgs: + keycloak: + +networks: + rdfm: +``` + +Before running the above services, you must first build the RDFM server container by running the following from the RDFM repository root folder: + +``` +docker build -f server/deploy/Dockerfile -t antmicro/rdfm-server:latest . +``` + +You can then run the services by running: + +``` +docker-compose -f server/deploy/docker-compose.keycloak.development.yml up +``` + +#### Keycloak configuration + +Further configuration on the Keycloak server is required before any requests are successfully authenticated. +First, navigate to the Keycloak Administration Console found at `http://localhost:8080/` and login with the initial credentials provided in Keycloak's configuration above (by default: `admin`/`admin`). + +Next, go to **Clients** and press **Create client**. +This client is required for the RDFM server to perform token validation. +The following settings must be set when configuring the client: +- **Client ID** - must match `RDFM_OAUTH_CLIENT_ID` provided in the RDFM server configuration, can be anything (for example: `rdfm-server-introspection`) +- **Client Authentication** - set to `On` +- **Authentication flow** - select only `Service accounts roles` + +After saving the client, go to the `Credentials` tab found under the client details. +Make sure the authenticator used is `Client Id and Secret`, and copy the `Client secret`. +This secret must be configured in the RDFM server under the `RDFM_OAUTH_CLIENT_SEC` environment variable. + +:::{note} +After changing the `docker-compose` variables, remember to restart the services (by pressing `Ctrl+C` and re-running the `docker-compose up` command). +::: + +Additionally, you must create proper client scopes to define which users have access to the read-only and read-write parts of the RDFM API. +To do this, navigate to the `Client scopes` tab and select `Create client scope`. +Create two separate scopes with the following names; the rest of the settings can be left as default (if required, you may also add a description to the scope): +- `rdfm_admin_ro` +- `rdfm_admin_rw` + +After restarting the services, the RDFM server will now validate requests against the Keycloak server. + +#### Adding an API user + +First, navigate to the Keycloak Administration Console found at `http://localhost:8080/` and login with the initial credentials provided in Keycloak's configuration above (by default: `admin`/`admin`). + +Next, go to **Clients** and press **Create client**. +This client will represent a user of the RDFM API. +The following settings must be set when configuring the client: +- **Client Authentication** - set to `On` +- **Authentication flow** - select only `Service accounts roles` + +After saving the client, go to the `Credentials` tab found under the client details. +Make sure the authenticator used is `Client Id and Secret`, and copy the `Client secret`. + +Finally, assign the required scope to the client: under the `Client scopes` tab, click `Add client scope` and select one of the two RDFM scopes: read-only `rdfm_admin_ro` or read-write `rdfm_admin_rw`. + +:::{note} +The newly-created client will now have access to the RDFM API. +To configure `rdfm-mgmt` to use this client, follow the [Configuration section](rdfm_manager.md#configuration) of the RDFM manager manual. +::: + +## Configuring HTTPS + +For simple deployments, the server can expose an HTTPS API directly without requiring an additional reverse proxy. +Configuration of the server's HTTPS can be done using the following environment variables: + +- `RDFM_SERVER_CERT` - path to the server's signed certificate +- `RDFM_SERVER_KEY` - path to the server's private key + +Both of these files must be accessible within the server Docker container. + +### HTTPS demo deployment + +:::{warning} +This demo deployment explicitly disables API authentication, and is only meant to be used as a reference on how to configure your particular deployment. +::: + +An example HTTPS deployment can be found in the `server/deploy/docker-compose.https.development.yml` file. +Before running it, you must execute the `tests/certgen.sh` in the `server/deploy/` directory: + +```bash +cd server/deploy/ +../tests/certgen.sh +``` + +This script generates a root CA and an associated signed certificate to be used for running the server. +The following files are generated: + +- `certs/CA.{crt,key}` - CA certificate/private key that is used as the root of trust +- `certs/SERVER.{crt,key}` - signed certificate/private key used by the server + +To run the deployment, you must first build the RDFM server container by running the following from the RDFM repository root folder: + +```bash +docker build -f server/deploy/Dockerfile -t antmicro/rdfm-server:latest . +``` + +You can then start the deployment by running: +```bash +docker-compose -f server/deploy/docker-compose.https.development.yml up +``` + +To verify the connection to the server, you must provide the CA certificate. +For example, when using `curl` to access API methods: + +```bash +curl --cacert server/deploy/certs/CA.crt https://127.0.0.1:5000/api/v1/devices +``` + +When using `rdfm-mgmt`: + +```bash +rdfm-mgmt --url https://127.0.0.1:5000/ \ + --cert server/deploy/certs/CA.crt \ + --no-api-auth \ + devices list +``` + diff --git a/_sources/rdfm_ota_manual.md.txt b/_sources/rdfm_ota_manual.md.txt new file mode 100644 index 0000000..e8aab16 --- /dev/null +++ b/_sources/rdfm_ota_manual.md.txt @@ -0,0 +1,113 @@ +# RDFM OTA Manual + +This chapter contains key information about the RDFM OTA update system. + +## Key concepts + +Below is a brief explanation of the key entities of the RDFM update system. + +### Devices + +From the server's point of view, a device is any system that is running an RDFM-compatible update client. +For example, see [RDFM Linux Device Client](./rdfm_linux_device_client.md). +Each device actively reports its metadata to the server: +- Currently running software version (`rdfm.software.version`) +- Device type (`rdfm.hardware.devtype`) +- Other client-specific metadata + +### Packages + +A package is any file that can be used by a compatible update client to update the running system. +From the server's point of view, update packages are simple binary blobs and no specific structure is enforced. +Each package has metadata assigned to it that indicates its contents. +The following metadata fields are mandatory for all packages: +- Software version (`rdfm.software.version`) - indicates the version of the contained software +- Device type (`rdfm.hardware.devtype`) - indicates the device type a package is compatible with + +The device type is used as the first filter when searching for a compatible update package. +Any package that does not match the device type reported by the update client will be considered incompatible. + +A package may also contain metadata with `requires:` clauses. +The `requires` clause is used to indicate dependencies on certain metadata properties of the device. +In its most basic form, it can be used to indicate a dependency on a certain system image to be installed for proper delta update installation. +For more complex use cases involving many intermediate update steps, it can also be used to enforce an order in which certain packages must be installed. + +### Groups + +A group consists of many assigned devices. Each group can also be assigned one or many packages. +The group itself also contains metadata about the group name, description, update policy, and other arbitrary information which can be used by custom frontends interacting with the server. + +### Update policy + +An update policy defines the target version the devices within a given group will be updated to. +The policy is a string with the syntax `,[arguments]`. +Required arguments depend on the specific policy being used. +Currently, the following policies are supported: +- `no_update` (**default**) - requires no arguments, the server will treat all devices within the group as up-to-date, and will not return any packages to devices requesting an update check. **This is the default update policy for all newly created groups**. +- `exact_match` - specifies that the server will attempt to install the target software version on each of the devices in the group. +Example usage: `exact_match,version1` - this specifies that the server will attempt to bring all of the devices to the software version `version1`. +This process may involve installing many intermediate packages, but the end result is a device that's running the specified version. +The server will use group-assigned packages when resolving the dependency graph required for reaching the target version. + +## Update resolution + +When resolving a path to the correct target version, the server utilizes only the group-assigned packages. +When a device is requesting an update check, a package dependency graph is created. +The edges of the graph correspond to different packages available during the update process (which are compatible with the device, as indicated by the `rdfm.hardware.devtype` field), while the nodes indicate the software versions (as indicated by the `rdfm.software.version` fields of each package). +Next, the group's update policy is queried, which indicates the target version/node each device should be attempting to reach. +The shortest path between the currently running node and the target node is used as instructions for how the server should lead the device to the specified version. + +## Example scenario: simple update assignment + +Consider a group with the following packages assigned: +- P0 - `devtype=foo`, `version=v1` +- P1 - `devtype=bar`, `version=v2` +- P2 - `devtype=baz`, `version=v3` + +The group is specified to update to version `v3` per the policy. Devices are reporting the following metadata: +- D0 - `devtype=foo`, `version=v3` +- D1 - `devtype=bar`, `version=v3` +- D2 - `devtype=baz`, `version=v3` + +In this scenario, devices `D0` and `D1` shall receive the update packages `P0` and `P1` respectively. +The device `D2` is considered up-to-date, as its version matches the target specified in the group policy. + +## Example scenario: downgrades + +Consider a group with the following packages assigned: +- P0 - `devtype=foo`, `version=v4` + +The group is specified to update to version `v4` per the policy. Devices are reporting the following metadata: +- D0 - `devtype=foo`, `version=v5` + +In this scenario, the device `D0` will receive the package `P0` to be installed next. + +## Example scenario: sequential updates + +Consider a group with the following packages assigned: +- P0 - `devtype=foo`, `version=v2`, `requires:version=v1` +- P1 - `devtype=foo`, `version=v3`, `requires:version=v2` + +The group is specified to update to version `v3` per the policy. Devices are reporting the following metadata: +- D0 - `devtype=foo`, `version=v1` + +In this scenario, the device `D0` will first be updated to the package `P0`, as it's the only package that is compatible (matching device type and different version than the one running on the device). +The package's only `requires` clause also matches against the device's metadata. + +After successful installation, during the next update check on the newly installed version (`v1`), the device will receive the next available package. +As the device is now reporting a version field of `v1` and the package's `requires:` clause passes, package `P1` becomes the next candidate package available for installation. +After successful instalation of `P1`, no more packages are available and the device is considered to be up-to-date. + +## Example scenario: delta updatess + +Consider a group with the following packages assigned: +- P0 (delta) - `devtype=foo`, `version=v5`, `rootfs=e6e2531..`, `requires:version=v0`, `requires:rootfs=2f646ac..` +- P1 (delta) - `devtype=foo`, `version=v5`, `rootfs=e6e2531..`, `requires:version=v2`, `requires:rootfs=6d9aee4..` + +The group is specified to update to version `v5` per the policy. Devices are reporting the following metadata: +- D0 - `devtype=foo`, `version=v0`, `rootfs=2f646ac..` +- D1 - `devtype=foo`, `version=v2`, `rootfs=6d9aee4..` + +In this scenario, devices `D0` and `D1` will receive packages `P0` and `P1` as updates respectively. +The packages themselves contain different binary contents, in this case a delta between a given base version's system partition (`v0` and `v2`) and the target (`v5`), but the end result is an identical system on both devices. +This way, many delta packages may be provided for updating a fleet consisting of a wide range of running versions. diff --git a/_sources/server_operation.md.txt b/_sources/server_operation.md.txt new file mode 100644 index 0000000..be124f5 --- /dev/null +++ b/_sources/server_operation.md.txt @@ -0,0 +1,111 @@ +# Server Integration flows + +This chapter describes the various integration flows between device clients and the RDFM Management server. + +## Device authentication + +At the start of their execution, all RDFM-compatible device clients shall authenticate with the server. +This shall be done by utilizing the `/api/v1/auth/device` endpoint. +For details on the request schema, refer to the [Server API Reference](api.rst) chapter. +An example request made to this endpoint is shown below: + +```json +{ + "metadata": { + "rdfm.hardware.devtype": "device-type", + "rdfm.software.version": "foo", + "rdfm.hardware.macaddr": "00:11:22:33:44:55", + } + "public_key": "", + "timestamp": 1694681536, +} +``` + +The JSON payload bytes must be signed by the device client with its securely stored RSA private key using PKCS #1 v1.5 signature with SHA-256 digest (function `RSASSA-PKCS1-V1_5-SIGN` defined in [RFC 8017](https://datatracker.ietf.org/doc/html/rfc8017#section-8.2.1)) +The calculated signature must then be attached, encoded as base64, to the authorization request in the header `X-RDFM-Device-Signature`. +If the server successfully validates the attached signature, the device will be registered in the server's database, if it wasn't previously registered already. +The device-specified MAC address is used as a unique identifier for this specific device. + +Before the device is authorized to access the RDFM API, it must be accepted first by an administrative entity interacting via a separate API with the RDFM server. +If the device was not accepted, or its acceptation status was revoked, the above request shall fail with the `401 Unauthorized` HTTP status code. **The device client must handle this status code gracefully**, for example by retrying the attempted request after a certain time has passed. + +Once the device is accepted into the RDFM server, the above request shall return a device-specific app token, that can be used to interact with device-side API endpoints. +The app token is not permanent, and will expire after a certain time period. +The device client must not make any assumptions about the length of the usability period, and instead should take a defensive approach to any requests made to the device-side API and reauthenticate when a response with the `401` status code is received. + +## Device update check + +Once authorized, a device client will have access to the device-side API of the RDFM server. +The device client is expected to regularly poll for updates by utilizing the `/api/v1/update/check` endpoint. + +In the update check request, the device client must provide all of its local metadata. +The metadata, which consists of simple key/value pairs, uniquely describes the set of software and/or hardware present on the device, but may also represent other transient +properties not persisted in storage, such as temperature sensor values. + +When making the update check, the device client is advised to provide all of its metadata to the server in the update request. +At the time of writing, below three metadata properties are mandatory and must be present in all update checks: + +- `rdfm.software.version` - version identifier of the currently running software package +- `rdfm.hardware.devtype` - device type, used for limiting package compatibility only to a subset of devices +- `rdfm.hardware.macaddr` - MAC address of the device's main network interface + +For future compatibility, device clients are advised to provide all of their metadata, not only the mandatory keys, in the update check request. +For more details on the structure of an update check request, consult the [Update API Reference](api.rst#post--api-v1-update-check) + +When a new package is available, the response shall be as described in the API Reference, and a one-time download URL to the package is generated. +The device client shall use this URL to download and install, or in the case of clients capable of stream installation, directly install the package. +The device client **MUST** verify the hash of the package as described in the update check response. + +Additionally, the device client **MUST** verify whether the package contents look sane before attempting to install it. +The server shall never return a package that is not of the same device type as the one advertised by the client. +However, the server itself currently imposes **no limitations** on the binary contents of the packages themselves. + +## Management WebSocket + +If supported, the device may also connect to a device management WebSocket. +This provides additional management functionality of registered devices, such as reverse shell and file transfer. +To connect to the WebSocket, a device token is required to be provided in the `Authorization` header of the WebSocket handshake. +The format of the header is exactly the same as in other device routes and is described [in the API Reference chapter](api.rst#api-authentication). + +The general management flow is as follows: +1. Device connects to the management WebSocket: `/api/v1/devices/ws` +1. Device sends a `CapabilityReport` message indicating the capabilities it supports +1. Device reads incoming management messages from the server and handles them accordingly +1. Device may also send messages to the server to notify of certain situations + +### RDFM Management Protocol + +The management protocol is message-oriented and all messages are expected to be sent in WebSocket text mode. +Each message is a JSON object in the form: +```json +{ + "method": "", + "arg0": "...", + "arg1": {"...": "..."}, + "...": "..." +} +``` + +The type of message sent is identified by the `method` field. +The rest of the object fields are unspecified and depend on the specific message type. +Schema for messages used by the server can be found in `common/communication/src/request_models.py`. +On error during handling of a request, the server may return a custom WebSocket status code. +A list of status codes used by the server can be found in `common/communication/src/rdfm/ws.py`. + +### Capabilities + +A capability indicates what management functionality is supported by a device. +The device should report its capabilities using the `CapabilityReport` message immediately after connecting to the server. +By default it is assumed that the device does not provide any capabilities. + +#### Capability - `shell` + +This capability indicates that a device supports spawning a reverse shell. +The following methods must be supported by the device: +- `shell_attach` + +A device with the `shell` capability should react to `shell_attach` messages by connecting to a shell WebSocket at `/api/v1/devices//shell/attach/`. +This establishes a connection between the requesting manager and the device. +This WebSocket can then be used to stream the contents of the shell session and receive user input. +The format of messages sent over this endpoint is implementation defined. +However, generally the shell output/input are simply sent as binary WebSocket messages containing the standard output/input as raw bytes. diff --git a/_sources/system_overview.md.txt b/_sources/system_overview.md.txt new file mode 100644 index 0000000..13bd7f3 --- /dev/null +++ b/_sources/system_overview.md.txt @@ -0,0 +1,44 @@ +# System Architecture + +The reference architecture of an RDFM system consists of: + +- `RDFM Management Server` - handles device connections, packages, deployment, remote device management +- `Devices` - devices connect to a central management server and utilize the exposed `REST API` and device-server RDFM protocol for providing remote management functionality +- `Users` - individual users that are authenticated and allowed read-only/read-write access to resources exposed by the server + +The system architecture can be visualized as follows: + +:::{figure-md} summary +![Architecture summary](images/summary.png) + +Summary of the system architecture +::: + +## HTTP REST API + +For functionality not requiring a persistent connection, the server exposes an HTTP API. A complete list of available endpoints can be found +in the [RDFM Server API Reference](api.rst) chapter. The clients use this API to perform update checks. + +## Device-server RDFM Protocol + +The devices also maintain a persistent connection to the RDFM Management Server by utilizing JSON-based messages sent over a WebSocket route. +This is used to securely expose additional management functionality without directly exposing device ports to the Internet. + +Each message sent using the RDFM protocol is structured as follows: + +```text +0 h ++----------------------------+ +| utf-8 encoded JSON message | ++----------------------------+ +``` + +The message is a UTF-8 encoded JSON object, where each message is distinguished by the mandatory ``'method'`` field. + +An example request sent to the server may look like: + +``{'method': 'capability_report', 'capabilities': {'shell': True}}`` + +A response from the server may look like: + +``{'method': 'alert', 'alert': {'devices': ['d1', 'd2']}}`` diff --git a/_static/fonts/0053ba6958e79f26751eabb555bd73d0.woff2 b/_static/fonts/0053ba6958e79f26751eabb555bd73d0.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..ab30100982f087a925abe34641b5909b145516b2 GIT binary patch literal 4728 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 G0%`y>M(+3k literal 0 HcmV?d00001 diff --git a/_static/fonts/029e176ad602329b4434892101db9cf3.woff2 b/_static/fonts/029e176ad602329b4434892101db9cf3.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..09e03c952296f0ba1e46dadb0ef38c0ad1950130 GIT binary patch literal 6044 zcmV;N7h~vmPew8T0RR9102iD94gdfE04SUQ02e?20RR9100000000000000000000 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 aZ;p=U&6{>stk@{AqPB76aF%E9h7|z0(@g*X literal 0 HcmV?d00001 diff --git a/_static/fonts/0948409a22b5979aa7e1ec20da9e61f1.woff2 b/_static/fonts/0948409a22b5979aa7e1ec20da9e61f1.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..6b0b4afef95479fff34683895510f679a5ebd45a GIT binary patch literal 5604 zcmV3CmxKT719J?g1=`;t*omE%DVl-mx!FcDRLO)EiWF%n+<2k; z{z!elnfh#A=$8#;!oA;6mu@pLR*NAVtg+#ALiW^f_ySP*^v)3)`y^K*nwsVo&Xm+~ zQXtm}w{@t54sii7Pz1cVK=4512TJ*thqv4N+ieFDwlzzlR>%WV4EEd^apPH1)UnWjm&>+$*LSaQ!hypx2nQCG zLhtfhOk#69N}~|A+V6@oo&h)=sa=$6+}x!>ZvQJIv~sZo+&sCcwbt$t^Z{`^3im?U zGzV5L7lcE?JmoT2GI(_I)OJU03Sp2nPVKu#I*j4}be8S^GaAA1$oHNPDqaW|ooxlN zOP8jRzIHA0*giz>5qU$T-9tM7gdIV7ZC;utU75B_nj){mF7m#G&0oIiJ(RT-N~x^K zRL1-MvkcA~-9uXWIyi_X*x~g?(J*1ckSeMpaTbHLGXMu=2)=BaC|-QY%QkzXn`Wr*+Hdm{ zHA3A`0ksP*@{k~PC2Od$rkZQ1wYE~E$&iIq%X=EDIQ0SXdb?br?u4G(P14&4$%